zookeeper实现分布式锁
前言
10个线程去下单,我们的库存只有5个,如果不加锁就会出现超卖
提示:以下是本篇文章正文内容,下面案例可供参考
准备样例
- 商品表,5个库存
- 再利用nginx做下负载均衡,使请求分配到2个tomcat里去,达到一个分布式效果。
- 准备好zookeeper,安装的方法我之前的博客有,使用也介绍过。
4. 准备压测工具Jmeter
项目的controller地址
启动服务8080和8090
可以清楚的看到
只有5个线程能够减库存,其他的都会提示库存不足
关键代码
@Configuration
public class CuratorCfg {
@Bean(initMethod = "start")
public CuratorFramework curatorFramework(){
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
CuratorFramework client = CuratorFrameworkFactory.newClient("10.20.0.171:2181", retryPolicy);
return client;
}
}
```java
@RestController
public class TestController {
@Autowired
private OrderService orderService;
@Value("${server.port}")
private String port;
@Autowired
CuratorFramework curatorFramework;
@PostMapping("/stock/deduct")
public Object reduceStock(Integer id) throws Exception {
//创建互斥锁,传入客户端,和路径
InterProcessMutex interProcessMutex = new InterProcessMutex(curatorFramework, "/product_" + id);
try {
// 阻塞等待,一直等知道加锁成功为止
interProcessMutex.acquire();
orderService.reduceStock(id);
} catch (Exception e) {
if (e instanceof RuntimeException) {
throw e;
}
}finally {
interProcessMutex.release();
}
return "ok:" + port;
}
}
原理分析
public InterProcessMutex(CuratorFramework client, String path)
{
this(client, path, new StandardLockInternalsDriver());
}
主要是这个类InterProcessMutex
加锁的主逻辑代码
private boolean internalLock(long time, TimeUnit unit) throws Exception
{
/*
Note on concurrency: a given lockData instance
can be only acted on by a single thread so locking isn't necessary
*/
Thread currentThread = Thread.currentThread();
LockData lockData = threadData.get(currentThread);
if ( lockData != null )
{
// re-entering
lockData.lockCount.incrementAndGet();
return true;
}
String lockPath = internals.attemptLock(time, unit, getLockNodeBytes());
if ( lockPath != null )
{
LockData newLockData = new LockData(currentThread, lockPath);
threadData.put(currentThread, newLockData);
return true;
}
return false;
}
这一块主要分为3步:
- 把当前线程作为key传进去,拿到lockData
- 如果lockPath不等于空,则加锁成功。把加锁对象和当前线程封装起来
- 等这个线程再去请求则会发现lockData不为空,直接自增,返回true代表已经拿到锁了。
我们来看这个方法
internals.attemptLock(time, unit, getLockNodeBytes());
对于容器节点,如果没有子节点,则会删除掉。
这块就是创建了个类型为容器节点的父节点。子节点是顺序节点
创建好节点之后,要去判断创建的这个节点是不是最小的那个
这个方法是拿到所有子节点并进行排序。
然后判断刚刚创建的节点是不是这里面最小的,最小的才能获得锁
zookeeper和redis实现分布式锁的区别