热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

分布式共享锁的设计

|date:20190410功能介绍针对某种资源,需要被整个系统的各台服务器共享访问,但是只允许一台服务器同时访问。比如说订单服务是做成

| date: 20190410

功能介绍


针对某种资源,需要被整个系统的各台服务器共享访问,但是只允许一台服务器同时访问。比如说订单服务是做成集群的,当两个以上结点同时收到一个相同订单的创建指令,这时并发就产生了,系统就会重复创建订单。而分布式共享锁就是解决这类问题。

##流程图
在这里插入图片描述

代码实现

/*** @Author feizhou* @Description 分布式锁模板* @Date 10:39 2019/4/9* @Param [key, actionLog, expireSecond]* @return java.lang.Boolean**/
public static Boolean distributedLock_v2(String key,String actionLog, long milliseconds,boolean isDelLock){RedisBaseDao redisDao = RedisUtil.getRedisDao();boolean isGetLock=false;String requestId = UUID.randomUUID().toString();try {isGetLock = redisDao.getDistributedLock(key,requestId , milliseconds);if(!isGetLock){logger.error("分布式锁拦截,不能重复操作,"+key+",actionLog="+actionLog);}return isGetLock;} catch (Exception e) {e.printStackTrace();if(e instanceof RedisException){logger.error("redis 分布式锁异常,可能存在重复操作的的可能性,key="+key+",actionLog="+actionLog+",e="+e);return true;}}finally {if(isGetLock&&isDelLock){try {redisDao.releaseDistributedLock(key,requestId);} catch (Exception e) {e.printStackTrace();logger.error("分布式锁释放锁失败,key="+key+",actionLog="+actionLog+","+e);}}}return false;
}


/*** 尝试获取分布式锁* @param lockKey 锁* @param requestId 请求标识* @param milliseconds 超期时间* @return 是否获取成功*/private static final Long RELEASE_SUCCESS = 1L;
public boolean getDistributedLock(String lockKey, String requestId, Long milliseconds) {return this.setNx(lockKey, requestId, milliseconds);
}
/*** 释放分布式锁* @param lockKey 锁* @param requestId 请求标识* @return 是否释放成功*/
public boolean releaseDistributedLock( String lockKey, String requestId) {return this.deleteKeyForSameValue(lockKey,requestId);
}public Boolean setNx( String key, String value,Long expireTime) {Boolean isSet = redisTemplate.execute(new RedisCallback() {@Overridepublic Boolean doInRedis(RedisConnection redisConnection) throws DataAccessException {//过期时间好处:即使服务器宕机了,也能保证锁被正确释放。//setNx原子性操作,防止同一把锁在同一时间可能被不同线程获取到Jedis jedis = (Jedis) redisConnection.getNativeConnection();String result = jedis.set(key, value, "nx", "px", expireTime);if("OK".equals(result)){return true;}return false;}});return isSet;
}
public Boolean deleteKeyForSameValue( String key, String value) {return redisTemplate.execute(new RedisCallback() {@Overridepublic Boolean doInRedis(RedisConnection redisConnection) throws DataAccessException {Jedis jedis = (Jedis) redisConnection.getNativeConnection();//删除key的时候,先判断该key对应的value是否等于先前设置的随机值,只有当两者相等的时候才删除该key//防止释放其他客户端获取到的锁//原子性操作String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";Object result = jedis.eval(script, Collections.singletonList(key), Collections.singletonList(value));if (RELEASE_SUCCESS.equals(result)) {return true;}return false;}});
}

方案优点


多个服务器竞争资源,需要排队,解决类似一个订单被多个服务器提交问题。


方案缺点


  • 试用与一主多从的redis集群,如果多主多从,不能解决共享锁问题
    -这个问题解决方案https://yq.aliyun.com/articles/674394,https://blog.csdn.net/chen_kkw/article/details/81433470
  • 同时当一主多从服务器,主机宕机,有丢失锁的风险,概率很小。
    • 场景
    • 在Redis的master节点上拿到了锁,但是这个加锁的key还没有同步到slave节点,master故障,发生故障转移,slave节点升级为master节点; 导致锁丢失。概率很小,可以不考虑。

待改善点


对待完善点进行列举,以便后续改进


其他说明


推荐阅读
  • 本文介绍了一个Java猜拳小游戏的代码,通过使用Scanner类获取用户输入的拳的数字,并随机生成计算机的拳,然后判断胜负。该游戏可以选择剪刀、石头、布三种拳,通过比较两者的拳来决定胜负。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • springmvc学习笔记(十):控制器业务方法中通过注解实现封装Javabean接收表单提交的数据
    本文介绍了在springmvc学习笔记系列的第十篇中,控制器的业务方法中如何通过注解实现封装Javabean来接收表单提交的数据。同时还讨论了当有多个注册表单且字段完全相同时,如何将其交给同一个控制器处理。 ... [详细]
  • 开发笔记:Java是如何读取和写入浏览器Cookies的
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Java是如何读取和写入浏览器Cookies的相关的知识,希望对你有一定的参考价值。首先我 ... [详细]
  • 在springmvc框架中,前台ajax调用方法,对图片批量下载,如何弹出提示保存位置选框?Controller方法 ... [详细]
  • Java SE从入门到放弃(三)的逻辑运算符详解
    本文详细介绍了Java SE中的逻辑运算符,包括逻辑运算符的操作和运算结果,以及与运算符的不同之处。通过代码演示,展示了逻辑运算符的使用方法和注意事项。文章以Java SE从入门到放弃(三)为背景,对逻辑运算符进行了深入的解析。 ... [详细]
  • JavaWeb中读取文件资源的路径问题及解决方法
    在JavaWeb开发中,读取文件资源的路径是一个常见的问题。本文介绍了使用绝对路径和相对路径两种方法来解决这个问题,并给出了相应的代码示例。同时,还讨论了使用绝对路径的优缺点,以及如何正确使用相对路径来读取文件。通过本文的学习,读者可以掌握在JavaWeb中正确找到和读取文件资源的方法。 ... [详细]
  • HashMap的相关问题及其底层数据结构和操作流程
    本文介绍了关于HashMap的相关问题,包括其底层数据结构、JDK1.7和JDK1.8的差异、红黑树的使用、扩容和树化的条件、退化为链表的情况、索引的计算方法、hashcode和hash()方法的作用、数组容量的选择、Put方法的流程以及并发问题下的操作。文章还提到了扩容死链和数据错乱的问题,并探讨了key的设计要求。对于对Java面试中的HashMap问题感兴趣的读者,本文将为您提供一些有用的技术和经验。 ... [详细]
  • 本文介绍了安全性要求高的真正密码随机数生成器的概念和原理。首先解释了统计学意义上的伪随机数和真随机数的区别,以及伪随机数在密码学安全中的应用。然后讨论了真随机数的定义和产生方法,并指出了实际情况下真随机数的不可预测性和复杂性。最后介绍了随机数生成器的概念和方法。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 本文介绍了Java后台Jsonp处理方法及其应用场景。首先解释了Jsonp是一个非官方的协议,它允许在服务器端通过Script tags返回至客户端,并通过javascript callback的形式实现跨域访问。然后介绍了JSON系统开发方法,它是一种面向数据结构的分析和设计方法,以活动为中心,将一连串的活动顺序组合成一个完整的工作进程。接着给出了一个客户端示例代码,使用了jQuery的ajax方法请求一个Jsonp数据。 ... [详细]
  • quartus管脚分配后需要保存吗_嵌入式必须会的一些硬件面试题,要试一试吗?你过来呀!...
    1、下面是一些基本的数字电路知识问题,请简要回答之。(1)什么是Setup和Hold时间?答:SetupHoldTime用于测试芯片对输入 ... [详细]
author-avatar
心胸宽大的榛子lcf
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有