前言
本文主要举例说明为什么要用分布式锁,以及介绍在Spring cloud 中用curator框架实现分布式锁。
学习项目地址
一、为什么要用分布式锁?
这个系统有 passager-api , order-service, pay-service 三个模块,其中 order-service 和 pay-service 是服务提供者,passager-api是消费者。(pay-service其实在这里没有用到,只要关注passager-api 和 order-service即可。)
order-service中有TblInventoryController
TblInventoryController 中的 decreaseStock 方法,会将商品表中表示库存数量的值减1,这个方法在扣减之前会取库存值判断是否大于0,如果小于等于0,则不会再扣减。
passager-api 中会调用这个方法,实现库存数量的正确扣减
如果passager-api只运行了一个实例,由于controller类默认都是单例模式,这个“扣减操作”顺序执行,不会有问题
如果passager-api运行了多个实例,order-service运行了多个实例,多个并发请求进行扣减,就可能出现库存被扣减到小于0的情况,也就是出现了异常。
加上分布式锁,可以使得一个时间点内只有一个扣减操作发生,避免扣减库存至负值的情况。
二、引入curator
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.6.0</version>
</dependency>
三、配置zookeeper
在启动类中,写了一个bean,连到本地的zookepper
@Bean
public CuratorFramework curatorFramework() {
ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 3);
CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient("localhost:2181", retryPolicy);
curatorFramework.start();
return curatorFramework;
}
另:可以在docker中启动zookeeper
docker run -d --name zookeeper -p 2181:2181 zookeeper
四、代码中加上分布式锁
代码如下(示例):
String lockStr = "/lock";
InterProcessMutex lock = new InterProcessMutex(client, lockStr);
try{
if(lock.acquire(10, TimeUnit.HOURS)){
System.out.println(userId +"获取锁成功");
iOrderService.decreaseStock();
}else{
System.out.println(userId +"未获取到锁");
}
} catch (Exception e){
e.printStackTrace();
} finally {
try{
lock.release();
System.out.println("释放资源");
}catch (Exception e){
e.printStackTrace();
}
}
说明
数据库使用的mysql,商品表 的建表语句在文件 tbl_inventory.sql 中。
程序运行后,要测试,访问的地址为:http://localhost:9001/webclient/test?userId=${id}
还可以再起一个实例,访问地址为:http://localhost:9000/webclient/test?userId=${id}
idea 另起一个实例,使用不同的端口号,是在 run/debug configuration 中, --server.port=****
然后用jmeter新建两个ThreadGroup并发访问,${id}从一个csv文件中读取。