互联网电商项目扣减库存就是个典型的分布式问题,如果简单的使用synchronized,ReentrantLock,在单机的环境下是可行的,但是互联网项目一般是很多服务器集群的,再继续使用synchronized,ReentrantLock就无法锁住了,那么就需要使用分布锁,常用的分布锁有三种1.基于数据库的cas,2.基于redis分布锁 3.基于zookeeper分布锁。本文介绍zookeeper实现分布锁。
1.安装zookeeper
https://zookeeper.apache.org/releases.html#download官网下载zookeeper,具体安装可以网上另行找文章看看,比如这篇
https://www.cnblogs.com/expiator/p/9853378.html
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
ZooKeeper就是个树形结构,可以设置key,value,节点类型包括:1、临时节点 2、持久节点 3、临时有序节点 4、持久有序节点,zookeeper分布锁其实创建的就是临时有序节点,每次创建一个临时有序节点时,比如0000001,就等于获取到了锁,创建0000002时需要监听0000001是否已经释放,如果没有释放就不能获取锁,释放了,才能获取锁。
2.模拟高并发扣减库存
有轮子尽量使用轮子了,使用开源的zookeeper客户端Curator来连接zookeeper,本文测试的时候使用的是springboot,添加相关依赖就可以了。
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>5.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.curator/curator-recipes -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.0.0</version>
</dependency>
package com.figo.demo.test;
import java.util.concurrent.CountDownLatch;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;
/**
* zookeeper实现分布锁 .
* @author figo .
* 20200614 .
*/
public class ZookeeperTest {
//假设库存是100
static int stock = 100;
public static void main(String[] args) {
//连接zookeeper
CuratorFramework cf = CuratorFrameworkFactory.newClient("127.0.0.1:2181", 5000, 4000,
new ExponentialBackoffRetry(1000, 3));
cf.start();
//互斥锁创建(创建的是zookeeper的有序临时节点)
InterProcessMutex lock = new InterProcessMutex(cf, "/testlocks");
//测试高并发扣减库存
testConcurrent(lock);
}
//100个线程同时扣减库存
public static final int threadNum = 100;
/*
* 测试高并发扣减库存 .
*/
public static void testConcurrent(InterProcessMutex lock) {
//定义一个计数器
CountDownLatch cdl = new CountDownLatch(1);
for (int a = 0; a < threadNum; a++) {
Thread thread = new Thread(new MyRunable(cdl,lock));
thread.start();
}
//计数器减一,总共就1,减一等于0,这个时候所有的线程开始同时执行,模拟高并发
cdl.countDown();
}
//定义runnable
public static class MyRunable implements Runnable {
private final CountDownLatch countDownLatch;
InterProcessMutex lockNew;
public MyRunable(CountDownLatch cdl,InterProcessMutex lock) {
countDownLatch = cdl;
lockNew=lock;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
//等待在这里,直到计数器数值为0开始执行
countDownLatch.await();
//获取锁
lockNew.acquire();
//扣减库存
stock = stock - 1;
//释放锁
lockNew.release();
//打印当前线程名称,库存剩余多少
System.out.println("threadName="+Thread.currentThread().getName()+",current stock=" + stock);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
加上锁以后,可以发现每个线程扣减后,库存是有序递减,不加锁,容易发现多个线程扣减完库存后,库存值是一样,这个就有问题了,就像100个人同时拍商品,总共100个库存,扣完后,还剩下几个。。。大家可以测试一下看看。