Spring boot集成分布式锁Redisson

需求

引入锁机制后基本满足了软件的并发需求,但是也有一些问题:

  1. 系统变慢了,当用户进行一些慢速的修改操作时(导入,批量等),其他用户进行同类操作需要等待解锁,为了解决这个问题,需要结合业务实际情况细化锁的粒度
  2. 不同的服务之间不能共享锁,导致分布式的环境小概率出现并发问题

之前的互斥锁:自定义注解+AOP实现互斥锁
以上情况随着业务量的增加会越来越明显,简单的互斥锁预计只能撑到年底,所以需要引入Redisson

改进互斥锁方案

最初的想法是改进原先的互斥锁,细化锁的粒度,简单的说就是各人取个人的锁。那么需要自己写存入锁和取出锁的规则,还要考虑很多并发的情况,属于造轮子。
后来CTO建议我用Redisson,了解后发现完美解决了问题,于是放弃了这个方案。

关于Redis

由于Redisson是基于Redis的,所以要先部署Redis,和其他应用一样,直接开一个Docker容器就行了,参考DockerHub Redis

注意:最初是在自己服务器上测试的,redis容器没加密码,结果过几天就被挖矿了,于是老老实实加上密码

redis加密码:redis.conf中修改两行

1
2
3
4
# 把host限制注释掉
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
/**
* @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地址不同,把地址放到对应的配置文件中了

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
/**
* 同步锁 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官方文档

文章目录
  1. 1. 需求
    1. 1.1. 改进互斥锁方案
    2. 1.2. 关于Redis
    3. 1.3. 添加Maven依赖
    4. 1.4. 添加Redisson配置类
    5. 1.5. 新版的切面代码