背景

为了保证数据的一致性,在一些业务处理中都会选择加锁来保证数据的一致性。在单机模式下我们通常选择使用synchronized等这种JAVA提供好的jvm锁来实现,但是在集群和分布式情况下,这种jvm级别的锁式无法满足我们的需求,因为一个服务部署在多台服务器上,这些服务器上的jvm是无法通讯的,所以我们需要一种方案来解决分布式情况下数据一致性。

在互联网公司,基本上企业内部都会有自己的一套分布式锁开发框架

前言

分布式锁一般有三种实现方式:

  1. 数据库乐观锁
  2. 基于redis实现分布式锁
  3. 基于zooKeeper实习哪分布式锁

本次讲着重介绍zooKeeper实现分布式锁,和与数据库和redis实习分布式锁的对比。

核心

zookeeper的分布式锁主要是依据临时节点和事件监听机制来完成的

curator框架实现zookeeper分布式锁

前提:需要搭建起zookeeper基础架构,并且使用gateway网关做负载均衡

1
2
3
4
5
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.0.1</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration
public class CuratorConfig {

@Value("${spring.cloud.zookeeper.connect-string}")
private String zookeeperConnectionString;

@Bean(initMethod = "start")
public CuratorFramework curatorFramework(){
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
return CuratorFrameworkFactory.newClient(zookeeperConnectionString, retryPolicy);
}

}

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

@Autowired
private StringRedisTemplate stringRedisTemplate;

@Autowired
private CuratorFramework curatorFramework;

private static AtomicInteger count = new AtomicInteger(0);

@Override
public String curatorDecuctStock() {
String returnResult = "";

InterProcessMutex interProcessMutex = new InterProcessMutex(curatorFramework, "/stockLock");
try {
interProcessMutex.acquire();

int stock = Integer.parseInt(Objects.requireNonNull(stringRedisTemplate.opsForValue().get("stock")));
if (stock > 0) {
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + "");
System.out.println("扣减库存成功,剩余库存:" + realStock + " 卖出数量为:" + count.addAndGet(1));
returnResult = "扣减库存成功";
} else {
System.out.println("扣减库存失败,库存不足");
returnResult = "扣减库存失败,库存不足";
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
interProcessMutex.release();
} catch (Exception e) {
e.printStackTrace();
}
}

return returnResult;
}


集群压测

  • jmeter压测
    • 100个线程并发
    • 延迟0秒
    • 循环4次
  • 总库存为200
  • redis单机本地部署
  • 三个服务集群
  • geteway(本地部署)轮询负载均衡
  • zookeeper做分布式锁(本地单机)

image-20210901220545870

1
2
3
4
5
6
7
8
9
10
// 第一台服务
扣减库存成功,剩余库存:1 卖出数量为:67
扣减库存失败,库存不足
// 第二台服务
扣减库存成功,剩余库存:0 卖出数量为:67
扣减库存失败,库存不足
// 第三台服务
扣减库存成功,剩余库存:2 卖出数量为:66
扣减库存失败,库存不足
//总卖出数量为:67+67+66=200

redisson(redis)分布式锁和curator(zookeeper)分布式锁对比

通过jmeter压测发现,在同样的并发请求,redis本地单机,zookeeper本地单机的情况下,redis的300线程的200库存量用时平均在3秒左右,而zookeeper的300线程的200库存量用时平均在28秒左右。

可以明显看的出来,针对本次的300线程200库存,redis单机和zookeeper单机

对比 zookeeper redis
时间 28秒 3秒
CAP CP AP
性能 较低
可靠 较低

参考:

https://blog.csdn.net/java_66666/article/details/81015302