目录
在分布式环境下,分布式锁可以保证在多个节点上的并发操作时数据的一致性和互斥性。分布式锁有多种实现方案,最常用的两种方案是:zookeeper和redis,本文介绍redis实现分布式锁方案。
本地锁的局限性
JDK中自带的synchronized及lock锁,这些锁都是本地锁,在一个节点服务(一个JVM)下可以实现"锁"的效果,一旦服务多实例,本地锁就会存在问题。下方示例演示并发对redis的值做自增操作,观察效果,步骤如下:
1、编写请求和服务接口
此代码的主要逻辑是redis中key=num的值每次+1(初始值是0),其中redisTest方法上加上本地锁(synchronized)
java
@GetMapping("/redisTest")
public GenericWebResult redisTest() {
Long result = this.blogContentService.redisTest();
log.info("设置redis中num值:{}",result);
return GenericWebResult.ok((Object) "测试成功",result);
}
java
@Override
public synchronized Long redisTest() throws BusinessException {
Long result = 0l;
String key = "num";
RBucket<Object> bucket = this.redissonClient.getBucket(key);
if(bucket != null) {
Long ori = (Long)bucket.get();
result = ori.longValue() + 1;
this.redissonClient.getBucket(key).set(result);
}
return result;
}
2、单个实例启动后,用jemter并发请求测试
单实例启动后,服务端口:8888,jemter通过100个线程并发请求访问后,redis中key=num的值是100,符合预期。

3、后台服务启动多个实例, 再次用jemter并发请求测试
后台服务启动了2个实例,端口分别是8888和9999,并通过网关gateway对外公布统一的请求地址,网关会自动均衡到端口是8888和9999的2个服务中(这部分的配置不在此文赘述,详情见网关Gateway-CSDN博客),网关服务的端口是7777,请求的统一地址是:http://localhost:7777/blog-content/blogContent/redisTest ,通过jmeter测试如下(num初始恢复到0):




执行后,发现redis中key=num的值变成了65,不是100,不符合预期,证明本地锁在分布式环境下是有局限性的,它只能保证在一个JVM下锁的有效性。
Redisson解决分布式锁问题
Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务,其中就包含了各种分布式锁的实现。使用Redisson实现分布式锁,需要如下步骤:
1、项目添加依赖
XML
<!-- redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-data-23</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-data-26</artifactId>
<version>3.47.0</version>
</dependency>
2、改造redisTest方法,引入锁
通过redissonClient.getLock获取锁,在执行业务逻辑前锁住lock.lock(),执行完业务逻辑后释放锁lock.unlock()
java
@Override
public Long redisTest() throws BusinessException {
Long result = 0l;
RLock lock = redissonClient.getLock("lock");
lock.lock();
//业务逻辑
try {
log.info("业务逻辑开始...");
Thread.sleep(1000);//模拟业务逻辑时长
String key = "num";
RBucket<Object> bucket = this.redissonClient.getBucket(key);
if(bucket != null) {
Long ori = (Long)bucket.get();
result = ori.longValue() + 1;
this.redissonClient.getBucket(key).set(result);
}
log.info("业务逻辑结束...");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
log.info("redis 解锁...");
}
return result;
}
3、和第一章节一样,后台服务同样启动2个实例,端口分别是8888和9999,用jmeter模拟并发请求网关端口。

从后台请求日志可以看出,2个服务每隔1秒获取到锁,然后执行业务逻辑(num++),最终num的值是100,符合预期,实现了分布式环境下锁的效果。