使用Zookeeper实现分布式锁

分布式场景下生成订单ID

产生问题

在分布式(集群)环境下,每台JVM不能实现同步,在分布式场景下使用时间戳生成订单号可能会重复

 

分布式情况下,怎么解决订单号生成不重复

1、使用分布式锁

2、提前生成好,订单号,存放在redis取。获取订单号,直接从redis中取。【互联网公司常规做法】

 

使用分布式锁生成订单号技术

1.使用数据库实现分布式锁

缺点:性能差、线程出现异常时,容易出现死锁

2.使用redis实现分布式锁

缺点:锁的失效时间难控制、容易产生死锁、非阻塞式、不可重入

3.使用zookeeper实现分布式锁

实现相对简单、可靠性强、使用临时节点,失效时间容易控制

 

什么是分布式锁

分布式锁一般用在分布式系统或者多个应用中,用来控制同一任务是否执行或者任务的执行顺序。在项目中,部署了多个tomcat应用,在执行定时任务时就会遇到同一任务可能执行多次的情况,我们可以借助分布式锁,保证在同一时间只有一个tomcat应用执行了定时任务。

 

使用Zookeeper实现分布式锁

Zookeeper实现分布式锁原理

使用zookeeper创建临时序列节点来实现分布式锁,适用于顺序执行的程序,大体思路就是创建临时序列节点,找出最小的序列节点,获取分布式锁,程序执行完成之后此序列节点消失,通过watch来监控节点的变化,从剩下的节点的找到最小的序列节点,获取分布式锁,执行相应处理,依次类推……

 

代码实现zookeeper分布式锁

1、Maven依赖pom.xml

	<dependencies>
		<dependency>
			<groupId>com.101tec</groupId>
			<artifactId>zkclient</artifactId>
			<version>0.10</version>
		</dependency>
	</dependencies>

2、创建ZookeeperLock接口

//锁 定义分布式锁	
public interface ZookeeperLock {
	//获取锁
	public void getLock();
	
	//释放锁
	public void unLock();
}

3、创建抽象类ZookeeperAbstractLock

//重构重复代码,将重复代码交给子类执行	
public abstract class ZookeeperAbstractLock implements ZookeeperLock{
	//zk连接地址
	private static final String ADDRESS = "127.0.0.1:2181";
	//创建zk连接
	protected ZkClient zkClient = new ZkClient(ADDRESS);
	//创建节点PATH
	protected static final String PATH = "/lock";
	
	protected CountDownLatch countDownLatch = null;
	
	
	public void getLock() {
		if(tryLock()){
			System.out.println("###成功获取锁###");
		}else{
			waitLock();
			getLock();
		}
	}

	//尝试获取锁
	abstract Boolean tryLock();
	//等待
	abstract void waitLock();
	public void unLock() {
		if(zkClient != null){
			zkClient.close();
			System.out.println("###成功释放锁资源###");
                        System.out.println();
		}
	}
	
}

4、创建实现类ZookeeperDistributeLock

public class ZookeeperDistributeLock extends ZookeeperAbstractLock {
	// 创建临时节点成功,返回true,创建失败,返回false
	Boolean tryLock() {
		try {
			zkClient.createEphemeral(PATH);
			return true;
		} catch (RuntimeException e) {
			//e.printStackTrace();
			return false;
		}
	}

	void waitLock() {
		IZkDataListener iZkDataListener = new IZkDataListener() {
			// 当节点发生删除时
			public void handleDataDeleted(String dataPath) throws Exception {
				if (countDownLatch != null) {
					// 唤醒await()
					countDownLatch.countDown();
					//System.out.println("节点发生删除");
				}
			}

			// 当节点发生改变时
			public void handleDataChange(String dataPath, Object data) throws Exception {
				System.out.println("节点发生改变");
			}
		};

		// 注册节点信息
		zkClient.subscribeDataChanges(PATH, iZkDataListener);
		if (zkClient.exists(PATH)) {
			countDownLatch = new CountDownLatch(1);
			try {
				// 等待,直到监听到节点被删除
				countDownLatch.await();
			} catch (InterruptedException e) {
				//e.printStackTrace();
			}
		}
		//删除事件通知
		zkClient.unsubscribeDataChanges(PATH, iZkDataListener);
	}

}

 

测试zookeeper分布式锁

//生成订单号规则   时间戳+业务id
public class CreateID {
	//全局订单id
	public static int count = 0;

	public String getNumber() {
		try {
			Thread.sleep(200);
		} catch (Exception e) {
		}
		SimpleDateFormat simpt = new SimpleDateFormat("yyyyMMddHHmmss");
		return simpt.format(new Date()) + ++count;
	}

}
//使用多线程模拟多用户下单
public class OrderService implements Runnable{
	private CreateID cid = new CreateID();
	private ZookeeperLock lock = new ZookeeperDistributeLock();
	public void run() {
		try {
			lock.getLock();
			getNumber();
		} catch (Exception e) {
			//e.printStackTrace();
		}finally {
			lock.unLock();
		}
	}

	public void getNumber() {
		String number = cid.getNumber();
		System.out.println(Thread.currentThread().getName() + ",生成订单ID:" + number);
	}

	public static void main(String[] args) {
		System.out.println("####生成唯一订单号###");
		for (int i = 0; i < 100; i++) {
			new Thread(new OrderService()).start();
		}

	}

}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值