Spring boot集成分布式锁Redisson
需求
引入锁机制后基本满足了软件的并发需求,但是也有一些问题:
- 系统变慢了,当用户进行一些慢速的修改操作时(导入,批量等),其他用户进行同类操作需要等待解锁,为了解决这个问题,需要结合业务实际情况细化锁的粒度
- 不同的服务之间不能共享锁,导致分布式的环境小概率出现并发问题
之前的互斥锁:自定义注解+AOP实现互斥锁
以上情况随着业务量的增加会越来越明显,简单的互斥锁预计只能撑到年底,所以需要引入Redisson
改进互斥锁方案
最初的想法是改进原先的互斥锁,细化锁的粒度,简单的说就是各人取个人的锁。那么需要自己写存入锁和取出锁的规则,还要考虑很多并发的情况,属于造轮子。
后来CTO建议我用Redisson,了解后发现完美解决了问题,于是放弃了这个方案。
关于Redis
由于Redisson是基于Redis的,所以要先部署Redis,和其他应用一样,直接开一个Docker容器就行了,参考DockerHub Redis
注意:最初是在自己服务器上测试的,redis容器没加密码,结果过几天就被挖矿了,于是老老实实加上密码
redis加密码:redis.conf中修改两行
1 2 3 4
| bind 127.0.0.1
requirepass XXXXXXXX
|
推荐:
Windows上的免费Redis可视化工具 AnotherRedisDesktopManager
收藏:
Windows上Redis的安装包 Redis-64
添加Maven依赖
pom.xml中添加
1 2 3 4 5
| <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.11.4</version> </dependency>
|
添加Redisson配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
@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地址不同,把地址放到对应的配置文件中了
1
| redisson.address=redis://127.0.0.1:6379
|
新版的切面代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
|
@Component @Profile(NOT_DEVELOPMENT) @Scope @Aspect @Order(1) public class LockAspect {
@Value("${spring.profiles.active}") private String active;
@Autowired private RedissonClient redissonClient;
@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官方文档