--- title: Spring boot集成分布式锁Redisson date: 2019-10-10 13:55:53 description: 在原有互斥锁基础上改进为细粒度锁 categories: [技术总结] tags: [Java, Spring boot, Redis, Redisson] --- ## 需求 引入锁机制后基本满足了软件的并发需求,但是也有一些问题: 1. 系统变慢了,当用户进行一些慢速的修改操作时(导入,批量等),其他用户进行同类操作需要等待解锁,为了解决这个问题,需要结合业务实际情况细化锁的粒度 2. 不同的服务之间不能共享锁,导致分布式的环境小概率出现并发问题 之前的互斥锁:[自定义注解+AOP实现互斥锁](/2019/05/21/spring-boot-aop-lock) 以上情况随着业务量的增加会越来越明显,简单的互斥锁预计只能撑到年底,所以需要引入Redisson ### 改进互斥锁方案 最初的想法是改进原先的互斥锁,细化锁的粒度,简单的说就是各人取个人的锁。那么需要自己写存入锁和取出锁的规则,还要考虑很多并发的情况,属于造轮子。 后来CTO建议我用Redisson,了解后发现完美解决了问题,于是放弃了这个方案。 ### 关于Redis 由于Redisson是基于Redis的,所以要先部署Redis,和其他应用一样,直接开一个Docker容器就行了,参考[DockerHub Redis](https://hub.docker.com/_/redis) > 注意:最初是在自己服务器上测试的,redis容器没加密码,结果过几天就被挖矿了,于是老老实实加上密码 redis加密码:redis.conf中修改两行 ```yml # 把host限制注释掉 bind 127.0.0.1 # 打开密码设置 requirepass XXXXXXXX ``` **`推荐:`** Windows上的免费Redis可视化工具 [AnotherRedisDesktopManager](http://electronjs.org/apps/anotherredisdesktopmanager) **`收藏:`** Windows上Redis的安装包 [Redis-64](https://www.nuget.org/packages/Redis-64/3.0.500) ### 添加Maven依赖 pom.xml中添加 ```xml org.redisson redisson 3.11.4 ``` ### 添加Redisson配置类 ```java /** * @author chenbin */ @Configuration public class RedissonConfiguration { @Value("${redisson.address}") private String address; @Bean public RedissonClient getRedisson() { Config config = new Config(); config.useSingleServer().setAddress(address); config.useSingleServer().setPassword("XXXXXXXX"); return Redisson.create(config); } } ``` 由于每个环境redis地址不同,把地址放到对应的配置文件中了 ```properties redisson.address=redis://127.0.0.1:6379 ``` ### 新版的切面代码 ```java /** * 同步锁 AOP * @author chenbin */ @Component @Profile(NOT_DEVELOPMENT) @Scope @Aspect @Order(1) public class LockAspect { @Value("${spring.profiles.active}") private String active; @Autowired private RedissonClient redissonClient; /** * Service层切点 用于记录错误日志 */ @Pointcut("@annotation(com.hongfund.efi.service.lock.ServiceLock)") public void lockAspect() { } @Around("lockAspect()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String accountBookId = request.getHeader("accountBookId"); if (StringUtils.isBlank(accountBookId)) { throw new ServiceException("账套校验失败!", ErrorCode.BAD_REQUEST); } RLock lock = redissonClient.getLock(active + "_" + accountBookId); boolean res = lock.tryLock(10, TimeUnit.SECONDS); Object obj; if (res) { try { obj = joinPoint.proceed(); } finally { lock.unlock(); } return obj; } throw new ServiceException("其他用户正在操作,请等待!", ErrorCode.BAD_REQUEST); } } ``` - 这里根据业务情况把锁细化到账套级别 - 为防止本地环境报错,设置为非测试环境过滤 - 更新后暂时没问题,还需要测试性能和参数调优后再决定是否上线 **`参考:`** [Redisson官方文档](https://github.com/redisson/redisson/wiki)