在分布式场景中,采用传统的锁并不能解决跨进程并发的问题,所以需要引入一个分布式锁,来解决多个节点之间的访问控制
一、Zookeeper如何解决分布式锁
基于Zookeeper的两种特性来实现分布式锁:
- 第一种,使用唯一节点特性实现分布式锁
- 第二种,使用有序节点实现分布式锁
二、使用唯一节点特性实现分布式锁
多个应用程序去抢占锁资源时,只需要在指定节点上创建一个 /Lock 节点,由于Zookeeper中节点的唯一性特性,使得只会有一个用户成功创建 /Lock 节点,剩
下没有创建成功的用户表示竞争锁失败。
问题:惊群效应
“惊群效应”,简单来说就是如果存在许多的客户端在等待获取锁,当成功获取到锁的进程释放该节点后,所有处于等待状态的客户端都会被唤醒(等待的方式自然是使用Watcher机制来监听/lock节点的删除事件),这个时候zookeeper在短时间内发送大量子节点变更事件给所有待获取锁的客户端,然后实际情况是只会有一个客户端获得锁。如果在集群规模比较大的情况下,会对zookeeper服务器的性能产生比较的影响。
三:使用有序节点实现分布式锁
因此为了解决惊群效应,可以采用Zookeeper的有序节点特性来实现分布式锁。
每个客户端都往指定的节点下注册一个临时有序节点,越早创建的节点,节点的顺序编
号就越小,那么我们可以判断子节点中最小的节点设置为获得锁。如果自己的节点不是所有子节点中最小的,意味着还没有获得锁。
不同于第一种方式性在于,每个节点只需要监听比自己小的前一个节点,当比自己小的节点删除以后,客户端会收到watcher事件,此时再次判断自己的节点是不是所有子节点中最小的,如果是则获得锁,否则就不断重复这个过程,这样就不会导致羊群效应,因为每个客户端只需要监控前一个节点。
有序节点实现分布式锁的流程
四:Curator实现分布式锁
curator对于锁这块做了一些封装,curator提供了InterProcessMutex 这样一个api。
具体的使用方法如下:
1、引入pom
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.2.0</version>
</dependency>
2、CuratorConfig
@Configuration
public class CuratorConfig {
@Bean
public CuratorFramework curatorFramework(){
CuratorFramework curatorFramework=CuratorFrameworkFactory
.builder()
.connectString("127.0.0.1:2181")
.sessionTimeoutMs(15000)
.connectionTimeoutMs(20000)
.retryPolicy(new ExponentialBackoffRetry(1000,10))
.build();
curatorFramework.start();
return curatorFramework;
}
}
3、 Controller,使用锁机制
@Scope(scopeName = "prototype")
@RestController
@RequestMapping("/goods-stock")
public class GoodsStockController {
@Autowired
IGoodsStockService goodsStockService;
@Autowired
CuratorFramework curatorFramework;
@GetMapping("{goodsNo}")
public String purchase(@PathVariable("goodsNo")Integer goodsNo) throws
Exception {
QueryWrapper<GoodsStock> queryWrapper=new QueryWrapper<>();
queryWrapper.eq("goods_no",goodsNo);
InterProcessMutex lock=new
InterProcessMutex(curatorFramework,"/Lock");
try {
lock.acquire(); //抢占锁(阻塞)
GoodsStock goodsStock=goodsStockService.getOne(queryWrapper);
Thread.sleep(new Random().nextInt(1000));
if(goodsStock==null){
return "指定商品不存在";
}
if(goodsStock.getStock().intValue()<1){
return "库存不够";
}
goodsStock.setStock(goodsStock.getStock() - 1);
boolean res = goodsStockService.updateById(goodsStock);
if (res) {
return "抢购书籍:" + goodsNo + "成功";
}
return "抢购失败";
}finally {
lock.release(); //释放锁
}
}
}
注:前面已经理解的Zookeeper实现分布式锁的原理,以及基于Curator完成了分布式锁的使用, Curator就是基于代码实现了这一过程。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)