1 ZooKeeper实现分布式锁
Zookeeper的数据存储结构就像一棵树,这棵树由节点组成,这种节点叫做Znode。
Zookeeper中节点分为4种类型:
1.持久节点 (PERSISTENT)
默认的节点类型。创建节点的客户端与zookeeper断开连接后,该节点依旧存在
2.持久顺序节点(PERSISTENT_SEQUENTIAL)
所谓顺序节点,就是在创建节点时,Zookeeper根据创建的时间顺序给该节点名称进行编号
3.临时节点(EPHEMERAL)
和持久节点相反,当创建节点的客户端与zookeeper断开连接后,临时节点会被删除
4.临时顺序节点(EPHEMERAL_SEQUENTIAL)
顾名思义,临时顺序节点结合和临时节点和顺序节点的特点:在创建节点时,Zookeeper根据创建的时间顺序给该节点名称进行编号;当创建节点的客户端与zookeeper断开连接后,临时节点会被删除
Zookeeper实现分布式锁的原理是基于Zookeeper的临时顺序节点,如下图:
客户端A和客户端B争抢分布式锁,其实就是在/my_lock节点下创建临时顺序节点,这个顺序节点由zk内部自行维护一个节点序号,序号最小则表示对应客户端获得锁。
Apache Curator是一个比较完善的ZooKeeper客户端框架,通过封装的一套高级API 简化了ZooKeeper的操作,其中就包括分布式锁的实现。
2 JavaAPI具体操作过程如下
第一步:在pom.xml中导入maven坐标
<dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.10</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>2.12.0</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>2.12.0</version> </dependency>
第二步:编写配置类
package com.oldlu.config; import org.apache.curator.RetryPolicy; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.ExponentialBackoffRetry; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ZkConfig { @Bean public CuratorFramework curatorFramework(){ RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3); CuratorFramework client = CuratorFrameworkFactory.builder() .connectString("localhost:2181") .sessionTimeoutMs(5000) .connectionTimeoutMs(5000) .retryPolicy(retryPolicy) .build(); client.start(); return client; } }
第三步:改造StockController
@Autowired private CuratorFramework curatorFramework; @GetMapping("/stock") public String stock() { InterProcessMutex mutex = new InterProcessMutex(curatorFramework,"/mylock"); try { //尝试获得锁 boolean locked = mutex.acquire(0, TimeUnit.SECONDS); if(locked){ int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock")); if(stock > 0){ stock --; redisTemplate.opsForValue().set("stock",stock+""); System.out.println("库存扣减成功,剩余库存:" + stock); }else { System.out.println("库存不足!!!"); } //释放锁 mutex.release(); }else{ System.out.println("没有获取锁,不能执行减库存操作!!!"); } }catch (Exception ex){ System.out.println("出现异常!!!"); } return "OK"; }