传统方式生成订单号ID
生成订单类
//生成订单类
public class OrderNumGenerator {
//全局订单id
public static int count = 0;
public String getNumber() {
SimpleDateFormat simpt = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
return simpt.format(new Date()) + "-" + ++count;
}
}
多线程生成订单号模拟高并发
package com.test.zookeeper.locktest;
public class MultithreadedOrderService implements Runnable {
private OrderNumGenerator orderNumGenerator = new OrderNumGenerator();
public void run() {
getNumber();
}
public void getNumber() {
String number = orderNumGenerator.getNumber();
try {
//阻塞线程
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ",生成订单ID:" + number);
}
public static void main(String[] args) {
System.out.println("####生成唯一订单号###");
for (int i = 0; i < 200; i++) {
new Thread(new MultithreadedOrderService()).start();
}
}
}
效果:
多线程情况下 会产生重复订单号的问题
怎么解决
使用锁解决线程安全问题,使用synchronized或者loca锁,下面使用的synchronized
Synchronized同步代码块方式
package com.test.zookeeper.locktest;
public class MultithreadedOrderService implements Runnable {
private OrderNumGenerator orderNumGenerator = new OrderNumGenerator();
public void run() {
getNumber();
}
public void getNumber() {
synchronized (this) {
String number = orderNumGenerator.getNumber();
try {
//阻塞线程
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ",生成订单ID:" + number);
}
}
public static void main(String[] args) {
System.out.println("####生成唯一订单号###");
for (int i = 0; i < 200; i++) {
new Thread(new MultithreadedOrderService()).start();
}
}
}
效果:
解决订单号重复
分布式场景下生成订单号
问题
在分布式(集群)环境下,每台JVM不能实现同步,在分布式场景下使用时间戳生成订单号可能会重复
分布式情况下,怎么解决订单号生成不重复
- 使用分布式锁
- 提前生成好,订单号,存放在redis取。获取订单号,直接从redis中取。
- 百度 uidGenerator
- 美团leaf
- Snowflake雪花算法
zookeeper分布式锁解决订单号重复问题
下面我们介绍使用zookeeper实现分布式锁解决上面的问题
Zookeeper实现分布式锁原理
使用zookeeper创建临时序列节点来实现分布式锁,适用于顺序执行的程序,大体思路就是创建临时序列节点,找出最小的序列节点,获取分布式锁,程序执行完成之后此序列节点消失,通过watch来监控节点的变化,从剩下的节点的找到最小的序列节点,获取分布式锁,执行相应处理,依次类推……
Maven依赖
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
创建Lock接口
package com.test.zookeeper.locktest;
public interface lock {
public void getLock(); //获取锁
public void unLock(); //释放锁
}
创建ZookeeperAbstractLock抽象类
package com.test.zookeeper.locktest;
import org.I0Itec.zkclient.ZkClient;
public abstract class ZookeeperAbstractLock implements lock {
// zk连接地址
private static final String CONNECTSTRING = "127.0.0.1:2181";
// 创建zk连接
protected ZkClient zkClient = new ZkClient(CONNECTSTRING);
protected static final String PATH = "/lock";
@Override
public void getLock() {
if (tryLock()) {
System.out.println("##获取lock锁的资源####");
} else {
// 等待
waitLock();
// 重新获取锁资源
getLock();
}
}
protected abstract boolean tryLock();
// 等待
abstract void waitLock();
@Override
public void unLock() {
if (zkClient!=null){
zkClient.close();
System.out.println("释放锁资源.......");
}
}
}
ZookeeperDistrbuteLock类
package com.test.zookeeper.locktest;
import org.I0Itec.zkclient.IZkDataListener;
import java.util.concurrent.CountDownLatch;
public class ZookeeperDistrbuteLock extends ZookeeperAbstractLock {
private CountDownLatch countDownLatch = null;
@Override
protected boolean tryLock() {
try {
zkClient.createEphemeral(PATH);
return true;
} catch (Exception e) {
return false;
}
}
@Override
void waitLock() {
IZkDataListener izkDataListener = new IZkDataListener() {
@Override
public void handleDataChange(String s, Object o) throws Exception {
}
@Override
public void handleDataDeleted(String s) throws Exception {
// 唤醒被等待的线程
if (countDownLatch != null) {
countDownLatch.countDown();
}
}
};
// 注册事件
zkClient.subscribeDataChanges(PATH, izkDataListener);
if (zkClient.exists(PATH)) {
countDownLatch = new CountDownLatch(1);
try {
countDownLatch.await();
} catch (Exception e) {
e.printStackTrace();
}
}
// 删除监听
zkClient.unsubscribeDataChanges(PATH, izkDataListener);
}
}
DistributedOrderService 类
package com.test.zookeeper.locktest;
import com.test.zookeeper.test.OrderNumGenerator;
public class DistributedOrderService implements Runnable {
private OrderNumGenerator orderNumGenerator = new OrderNumGenerator();
private lock lock = new ZookeeperDistrbuteLock();
public void run() {
getNumber();
}
public void getNumber() {
try {
lock.getLock();
Thread.sleep(1000);
String number = orderNumGenerator.getNumber();
System.out.println(Thread.currentThread().getName() + ",生成订单ID:" + number);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unLock();
}
}
public static void main(String[] args) {
System.out.println("####生成唯一订单号###");
for (int i = 0; i < 300; i++) {
new Thread( new DistributedOrderService()).start();
}
}
}
效果:
解决分布式环境订单号重复