zookeeper 下载地址 :
https://mirrors.cnnic.cn/apache/zookeeper/zookeeper-3.4.14/
java 项目中引入依赖
<!-- zookeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.3</version>
</dependency>
zk编码序列化工具
package com.gpdi.operatingunit.test;
import org.I0Itec.zkclient.exception.ZkMarshallingError;
import org.I0Itec.zkclient.serialize.ZkSerializer;
import java.io.UnsupportedEncodingException;
/**
* @description: zk编码序列化工具
* @author: Lxq
* @date: 2020/1/10 16:34
*/
public class MyZkSerializer implements ZkSerializer {
String charset = "UTF-8";
@Override
public byte[] serialize(Object obj) throws ZkMarshallingError {
try {
return String.valueOf(obj).getBytes(charset);
} catch (UnsupportedEncodingException e) {
throw new ZkMarshallingError(e);
}
}
@Override
public Object deserialize(byte[] bytes) throws ZkMarshallingError {
try {
return new String(bytes, charset);
} catch (UnsupportedEncodingException e) {
throw new ZkMarshallingError(e);
}
}
}
模拟场景,分布式情况下高并发生成订单
订单接口
package com.gpdi.operatingunit.test.service;
/**
* @description: 创建订单服务
* @author: Lxq
* @date: 2020/1/10 17:01
*/
public interface OrderService {
/**
* 创建订单
*/
void createOrder();
}
订单实现类
package com.gpdi.operatingunit.test.service;
import com.gpdi.operatingunit.test.ZKDistributeLockPro;
import java.util.concurrent.locks.Lock;
/**
* @description: 创建订单的实现类
* @author: Lxq
* @date: 2020/1/10 17:02
*/
public class OrderServiceImpl implements OrderService {
private static OrderCodeGenerator ocg = new OrderCodeGenerator();
/**
* 使用分布式锁
*/
private static Lock lock = new ZKDistributeLockPro("/QQQQQQ");
@Override
public void createOrder() {
String orderCode = null;
// 获取订单号
try {
lock.lock();
orderCode = ocg.getOrderCode();
} finally {
lock.unlock();
}
System.out.println(Thread.currentThread().getName() + "-------------" + orderCode);
}
}
订单单号生成工具类:
package com.gpdi.operatingunit.test.service;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @description: 生成订单的工具类
* @author: Lxq
* @date: 2020/1/10 17:14
*/
public class OrderCodeGenerator {
private static int i = 0;
public String getOrderCode() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-");
return sdf.format(new Date()) + ++i;
}
}
分布式锁的代码实现(首先创建一个持久性父节点LockPath,然后每个要获锁的线程都要在这个节点下面创建临时顺序节点。由于zk节点是按照创建的顺序依次递增的,为了确保公平,可以简单规定,编码最小的那个节点就表示获取到锁,因此,每个线程在尝试占用锁之前,首先要判断自己的排号是否是最小的,若是则获取到锁,不是的话就等待前面节点执行完之后通知)
package com.gpdi.operatingunit.test;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
/**
* @description: 分布式锁升级版
* @author: Lxq
* @date: 2020/1/10 23:09
*/
public class ZKDistributeLockPro implements Lock {
private String LockPath;
private ZkClient client;
/**
* 当前节点路径
*/
private ThreadLocal<String> currentPath = new ThreadLocal<>();
/**
* 前一个节点的路径
*/
private ThreadLocal<String> beforePath = new ThreadLocal<>();
public ZKDistributeLockPro(String lockPath) {
super();
this.LockPath = lockPath;
client = new ZkClient("localhost:2181");
client.setZkSerializer(new MyZkSerializer());
if (!client.exists(LockPath)) {
client.createPersistent(LockPath);
}
}
@Override
public boolean tryLock() {
if (this.currentPath.get() == null) {
// 创建临时节点
currentPath.set(this.client.createEphemeralSequential(LockPath + "/", "aaa"));
}
// 获取所有的子节点
List<String> children = this.client.getChildren(LockPath);
// 排序
Collections.sort(children);
// 判断当前节点是否是最小
if (currentPath.get().equals(LockPath + "/" + children.get(0))) {
return true;
} else {
// 获取前一个节点,得到字节的索引号
int curIndex = children.indexOf(currentPath.get().substring(LockPath.length() + 1));
beforePath.set(LockPath + "/" + children.get(curIndex - 1));
}
return false;
}
@Override
public void lock() {
if (!tryLock()) {
//阻塞
waitForLock();
lock();
}
}
private void waitForLock() {
// 怎么让自己阻塞
CountDownLatch c = new CountDownLatch(1);
//注册watcher
IZkDataListener listener = new IZkDataListener() {
@Override
public void handleDataChange(String s, Object o) throws Exception {
}
@Override
public void handleDataDeleted(String s) throws Exception {
//监听到节点删除,唤醒
System.out.println("监听到节点被删除");
c.countDown();
}
};
// 注册进去
client.subscribeDataChanges(this.beforePath.get(), listener);
if (client.exists(this.beforePath.get())) {
try {
// 阻塞
c.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 解除注册
client.unsubscribeDataChanges(this.beforePath.get(), listener);
}
@Override
public void unlock() {
// 删除节点
this.client.delete(this.currentPath.get());
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
@Override
public void lockInterruptibly() throws InterruptedException {
}
@Override
public Condition newCondition() {
return null;
}
}
测试以及测试结果:
package com.gpdi.operatingunit.test;
import com.gpdi.operatingunit.test.service.OrderService;
import com.gpdi.operatingunit.test.service.OrderServiceImpl;
import java.util.concurrent.CountDownLatch;
/**
* @description:
* @author: Lxq
* @date: 2020/1/10 16:57
*/
public class DistributDemo {
public static void main(String[] args) {
// 模拟多个并发创建订单
int currs = 10;
CountDownLatch cbl = new CountDownLatch(currs);
for (int i = 0; i < currs; i++) {
new Thread(new Runnable() {
@Override
public void run() {
OrderService os = new OrderServiceImpl();
cbl.countDown();
try {
cbl.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
os.createOrder();
}
}).start();
}
}
}