首页 > 编程知识 正文

java实现分布式锁,springboot实现分布式

时间:2023-05-04 16:35:55 阅读:147330 作者:805

参考链接:

3359 blog.csdn.net/Wu Zhiwei 549/article/details/80692278

3359 blog.52 it style.VIP/archives/3202 /

首先说明zookeeper实现分布式锁定的原理。 这里可以看到csdn非常好的文章。 如果不想看到原理,请跳过下面的具体代码实现进行查看

在独立的APP应用程序体系结构中,秒杀案例使用ReentrantLcok或synchronized实现秒杀商品互斥的目的。 但是,在分布式系统中,为了实现相同的功能,多个机器并行存在。 也就是说,在多进程中,如果使用上述JDK提供的进程锁同时访问数据库资源,则商品可能会畅销。 因此,必须实现自己的分布式锁定。

实现分布式锁定所需的功能:

高可用性、高性能获取锁定和释放锁定

在分布式系统环境中,一个方法或变量一次只能在一个线程中操作

如果锁定已禁用,并且网络中断或瘫痪,无法解除锁定,则必须删除锁定以防止死锁

如果未能获取阻塞锁定特性,即锁定,则继续等待获取锁定

如果无法获取无阻塞锁定特性,即锁定,则直接返回,获取锁定失败

它具有可重载的特性,可以在一个线程中多次获取同一锁。 例如,如果一个线程正在运行锁定方法,并且该方法调用了另一个需要相同锁定的方法,则该线程可以直接执行调用的方法,而无需重新获取锁定

在以前的秒杀案例中,针对分布式锁介绍过几种实现方式:

基于数据库的分布式锁定

基于Redis的分布式锁定

基于Zookeeper的分布式锁定

前两项在分布式生产环境中不特别推荐。 高并发会降低数据库锁的性能,Redis在锁定时间限制和缓存一致性方面存在问题。 这里主要介绍Zookeeper是如何实现分布式锁定的。

Zookeeper的数据存储结构就像由一个称为Znode的节点组成的树。

Znode有四种类型。

1 .永久节点(永久)

的默认节点类型。 创建节点的客户端与zookeeper断开连接后,该节点仍然存在。

2 .永久节点顺序节点(PERSISTENT_SEQUENTIAL ) )。

序列节点是创建节点时,根据Zookeeper创建的时间序列对其节点名称进行编号。

3 .临时节点(EPHEMERAL ) ) ) ) ) ) )。

与永久节点相反,如果创建节点的客户端与zookeeper断开连接,则临时节点将被删除。

4 .临时顺序节点(EPHEMERAL_SEQUENTIAL ) () ) ) )。

zjdnp,临时顺序节点的结合和临时节点与顺序节点的特点:创建节点时,Zookeeper根据创建的时间序列对其节点名称进行编号; 如果创建节点的客户端与zookeeper断开连接,则临时节点将被删除。

Zookeeper分布式锁定原理

Zookeeper分布式锁是应用了临时序列节点的锁。 具体怎么实现? 让我们看看详细的步骤。

获取锁定

首先,在Zookeeper上创建永久节点ParentLock。 如果第一个客户端获得锁定,则必须在名为ParentLock的节点下创建临时顺序节点Lock1。

之后,客户端1查找并重新排序ParentLock下的所有临时顺序的节点,判断自己创建的节点Lock1是否是顺序最快的。 对于第一个节点,已成功获取锁定。

此时,如果另一个客户端Client2来获取锁定,请在ParentLock下载中创建临时序列节点Lock2。

客户端2查找并排序ParentLock下的所有临时顺序节点,并确定自己创建的节点Lock2是否是顺序最快的节点,结果表明节点Lock2不是最小的。

因此,客户端2在Lock1中注册Watcher,该Lock1是比其上级节点,以便监听是否存在Lock1节点。 这意味着客户端2未能夺取锁定,进入等待状态。

此时,如果另一个客户端Client3来获取锁定,请在ParentLock上下载它,然后创建临时序列节点Lock3。

客户端3查找并排序ParentLock下的所有临时顺序节点,并确定自己创建的节点Lock3是否是顺序最早的节点,结果同样表明节点Lock3不是最小的。

因此,客户端3为了监听是否存在Lock2节点,在Lock2是比其高的节点注册Watcher。 这意味着Client3也同样未能夺取锁定,进入等待状态。

这样,客户端1被锁定,客户端2监听到Lock1,客户端3监听到Lock2。 这就形成了一个队列,就像Java的ReentrantLock依赖它一样

解除锁定

要解除锁定,有以下两种情况:

1 .任务完成,客户端显示释放

任务完成后,客户端1将显示命令以调用删除节点Lock1。

2 .客户端在执行任务时崩溃

p>

获得锁的Client1在任务执行过程中,如果Duang的一声崩溃,则会断开与Zookeeper服务端的链接。根据临时节点的特性,相关联的节点Lock1会随之自动删除。

由于Client2一直监听着Lock1的存在状态,当Lock1节点被删除,Client2会立刻收到通知。这时候Client2会再次查询ParentLock下面的所有节点,确认自己创建的节点Lock2是不是目前最小的节点。如果是最小,则Client2顺理成章获得了锁。

同理,如果Client2也因为任务完成或者节点崩溃而删除了节点Lock2,那么Client3就会接到通知。

最终,Client3成功得到了锁。

Springboot实现zookeeper分布式锁
这里使用第三方库Curator来实现对zookeeper的操作
Curator是Netflix公司开源的一套zookeeper客户端框架,解决了很多Zookeeper客户端非常底层的细节开发工作,包括连接重连、反复注册Watcher和NodeExistsException异常等等。

Curator的maven如下

<!-- zookeeper 分布式锁、注意zookeeper版本 这里对应的是3.4.6 对应版本查询网址https://curator.apache.org/zk-compatibility.html--> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>2.10.0</version> </dependency>

Zookeeper锁的帮助工具类ZkLockUtil

import java.util.concurrent.TimeUnit;import org.apache.curator.RetryPolicy;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 科帮网 By https://blog.52itstyle.com */public class ZkLockUtil{ private static String address = "123.229.94.231:2181"; public static CuratorFramework client; static{ RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); client = CuratorFrameworkFactory.newClient(address, retryPolicy); client.start(); } /** * 私有的默认构造子,保证外界无法直接实例化 */ private ZkLockUtil(){}; /** * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例 * 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载 * 针对一件商品实现,多件商品同时秒杀建议实现一个map */ private static class SingletonHolder{ /** * 静态初始化器,由JVM来保证线程安全 * 参考:http://ifeve.com/zookeeper-lock/ * 这里建议 new 一个 */ private static InterProcessMutex mutex = new InterProcessMutex(client, "/curator/lock"); } public static InterProcessMutex getMutex(){ return SingletonHolder.mutex; } //获得了锁 public static boolean acquire(long time, TimeUnit unit){ try { return getMutex().acquire(time,unit); } catch (Exception e) { e.printStackTrace(); return false; } } //释放锁 public static void release(){ try { getMutex().release(); } catch (Exception e) { e.printStackTrace(); } }}

service实现调用

@Transactionalpublic Result startSeckilZksLock(long seckillId, long userId) {boolean res=false;try {res = ZkLockUtil.acquire(3,TimeUnit.SECONDS);if(res){String nativeSql = "SELECT number FROM seckill WHERE seckill_id=?";Object object = dynamicQuery.nativeQueryObject(nativeSql, new Object[]{seckillId});Long number = ((Number) object).longValue();if(number>0){SuccessKilled killed = new SuccessKilled();killed.setSeckillId(seckillId);killed.setUserId(userId);killed.setState((short)0);killed.setCreateTime(new Timestamp(new Date().getTime()));dynamicQuery.save(killed);nativeSql = "UPDATE seckill SET number=number-1 WHERE seckill_id=? AND number>0";dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{seckillId});}else{return Result.error(SeckillStatEnum.END);}}else{return Result.error(SeckillStatEnum.MUCH);}} catch (Exception e) {e.printStackTrace();} finally{if(res){//释放锁ZkLockUtil.release();}}return Result.ok(SeckillStatEnum.SUCCESS);}

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。