如何在springcloud分布式系统中实现分布式锁?
一、简介
一般来说,对数据进行加锁时,程序先通过acquire获取锁来对数据进行排他访问,然后对数据进行一些列的操作,最后需要释放锁。Redis 本身用 watch命令进行了加锁,这个锁是乐观锁。使用 watch命令对于频繁访问的键会引起性能的问题。
二、redis命令介绍
- SETNX命令(SET if Not eXists)
- 当且仅当 key 不存在,将 key 的值设为 value ,并返回1;若给定的 key 已经存在,则 SETNX 不做任何动作,并返回0。
- SETEX命令
- 设置超时时间
- GET命令
- 返回 key 所关联的字符串值,如果 key 不存在那么返回特殊值 nil 。
- DEL命令
- 删除给定的一个或多个 key ,不存在的 key 会被忽略。
三、实现思路
由于redis的setnx命令天生就适合用来实现锁的功能,这个命令只有在键不存在的情况下为键设置值。获取锁之后,其他程序再设置值就会失败,即获取不到锁。获取锁失败。只需不断的尝试获取锁,直到成功获取锁,或者到设置的超时时间为止。
另外为了防治死锁,即某个程序获取锁之后,程序出错,没有释放,其他程序无法获取锁,从而导致整个分布式系统无法获取锁而导致一系列问题,甚至导致系统无法正常运行。这时需要给锁设置一个超时时间,即setex命令,锁超时后,从而其它程序就可以获取锁了。
四、编码实现
本文采用springboot结合redis 取实现的,所以你需要装一个redis。
- 首先引入创建springboot工程,引入redis 。
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
- <scope>test</scope>
- </dependency>
- <!-- 开启web-->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
- <!-- redis-->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- </dependency>
2.创建一个锁类
- /**
- * 全局锁,包括锁的名称
- * Created by fangzhipeng on 2017/4/1.
- */
- public class Lock {
- private String name;
- private String value;
- public Lock(String name, String value) {
- this.name = name;
- this.value = value;
- }
- public String getName() {
- return name;
- }
- public String getValue() {
- return value;
- }
- }
3.创建分布式锁的具体方法,思路已经说清楚了,代码注释也写好了,就不讲解了。
- import org.apache.commons.lang.StringUtils;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.data.redis.core.StringRedisTemplate;
- import org.springframework.data.redis.core.ValueOperations;
- import org.springframework.stereotype.Component;
- import java.util.concurrent.TimeUnit;
- /**
- * Created by fangzhipeng on 2017/4/1.
- */
- @Component
- public class DistributedLockHandler {
- private static final Logger logger = LoggerFactory.getLogger(DistributedLockHandler.class);
- private final static long LOCK_EXPIRE = 30 * 1000L;//单个业务持有锁的时间30s,防止死锁
- private final static long LOCK_TRY_INTERVAL = 30L;//默认30ms尝试一次
- private final static long LOCK_TRY_TIMEOUT = 20 * 1000L;//默认尝试20s
- @Autowired
- private StringRedisTemplate template;
- /**
- * 尝试获取全局锁
- *
- * @param lock 锁的名称
- * @return true 获取成功,false获取失败
- */
- public boolean tryLock(Lock lock) {
- return getLock(lock, LOCK_TRY_TIMEOUT, LOCK_TRY_INTERVAL, LOCK_EXPIRE);
- }
- /**
- * 尝试获取全局锁
- *
- * @param lock 锁的名称
- * @param timeout 获取超时时间 单位ms
- * @return true 获取成功,false获取失败
- */
- public boolean tryLock(Lock lock, long timeout) {
- return getLock(lock, timeout, LOCK_TRY_INTERVAL, LOCK_EXPIRE);
- }
- /**
- * 尝试获取全局锁
- *
- * @param lock 锁的名称
- * @param timeout 获取锁的超时时间
- * @param tryInterval 多少毫秒尝试获取一次
- * @return true 获取成功,false获取失败
- */
- public boolean tryLock(Lock lock, long timeout, long tryInterval) {
- return getLock(lock, timeout, tryInterval, LOCK_EXPIRE);
- }
- /**
- * 尝试获取全局锁
- *
- * @param lock 锁的名称
- * @param timeout 获取锁的超时时间
- * @param tryInterval 多少毫秒尝试获取一次
- * @param lockExpireTime 锁的过期
- * @return true 获取成功,false获取失败
- */
- public boolean tryLock(Lock lock, long timeout, long tryInterval, long lockExpireTime) {
- return getLock(lock, timeout, tryInterval, lockExpireTime);
- }
- /**
- * 操作redis获取全局锁
- *
- * @param lock 锁的名称
- * @param timeout 获取的超时时间
- * @param tryInterval 多少ms尝试一次
- * @param lockExpireTime 获取成功后锁的过期时间
- * @return true 获取成功,false获取失败
- */
- public boolean getLock(Lock lock, long timeout, long tryInterval, long lockExpireTime) {
- try {
- if (StringUtils.isEmpty(lock.getName()) || StringUtils.isEmpty(lock.getValue())) {
- return false;
- }
- long startTime = System.currentTimeMillis();
- do{
- if (!template.hasKey(lock.getName())) {
- ValueOperations<String, String> ops = template.opsForValue();
- ops.set(lock.getName(), lock.getValue(), lockExpireTime, TimeUnit.MILLISECONDS);
- return true;
- } else {//存在锁
- logger.debug("lock is exist!!!");
- }
- if (System.currentTimeMillis() - startTime > timeout) {//尝试超过了设定值之后直接跳出循环
- return false;
- }
- Thread.sleep(tryInterval);
- }
- while (template.hasKey(lock.getName())) ;
- } catch (InterruptedException e) {
- logger.error(e.getMessage());
- return false;
- }
- return false;
- }
- /**
- * 释放锁
- */
- public void releaseLock(Lock lock) {
- if (!StringUtils.isEmpty(lock.getName())) {
- template.delete(lock.getName());
- }
- }
- }
4.用法:
- @Autowired
- DistributedLockHandler distributedLockHandler;
- Lock lock=new Lock("lockk","sssssssss);
- if(distributedLockHandler.tryLock(lock){
- doSomething();
- distributedLockHandler.releaseLock();
- }
五、注意点
在使用全局锁时为了防止死锁采用 setex命令,这种命令需要根据具体的业务具体设置锁的超时时间。另外一个就是锁的粒度性。比如在redis实战中有个案列,为了实现买卖市场交易的功能,把整个交易市场都锁住了,导致了性能不足的情况,改进方案只对买卖的商品进行加锁而不是整个市场。
如何在springcloud分布式系统中实现分布式锁?的更多相关文章
- java就业指南 zookeeper分布式系统 zookeeper实现分布式锁 有用
目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题.分布式的CAP理论告诉我们“任何一个 分布式系统都无法同时满足一致性(Consistency).可用性 ...
- redis中的分布式锁
分布式锁的实现场景 在平时的开发中,对于高并发的开发场景,我们不可避免要加锁进行处理,当然redis中也是不可避免的,下面是我总结出来的几种锁的场景 Redis分布式锁方案一 使用Redis实现分布式 ...
- 分布式锁之三:Redlock实现分布式锁
之前写过一篇文章<如何在springcloud分布式系统中实现分布式锁?>,由于自己仅仅是阅读了相关的书籍,和查阅了相关的资料,就认为那样的是可行的.那篇文章实现的大概思路是用setNx命 ...
- 如何用Redlock实现分布式锁
转载请标明出处: http://blog.csdn.net/forezp/article/details/70305336 本文出自方志朋的博客 之前写过一篇文章<如何在springcloud分 ...
- 基于zookeeper或redis实现分布式锁
前言 在分布式系统中,分布式锁是为了解决多实例之间的同步问题.例如master选举,能够获取分布式锁的就是master,获取失败的就是slave.又或者能够获取锁的实例能够完成特定的操作. 目前比较常 ...
- Spring Cloud分布式微服务系统中利用redssion实现分布式锁
在非分布式系统中要实现锁的机制很简单,利用java.util.concurrent.locks包下的Lock和关键字synchronized都可以实现.但是在分布式系统中,如何实现各个单独的微服务需要 ...
- 分布式锁中的王者方案-Redisson
上篇讲解了如何用 Redis 实现分布式锁的五种方案,但我们还是有更优的王者方案,就是用 Redisson. 缓存系列文章: 缓存实战(一):20 图 |6 千字|缓存实战(上篇) 缓存实战(二):R ...
- SpringCloud(5)之分布式锁实现
01为什么用分布式锁 在讨论这个问题之前,我们先来看一个业务场景:系统A是一个电商系统,目前是一台机器部署,系统中有一个用户下订单的接口,但是用户下订单之前一定要去检查一下库存,确保库存足够了才会给用 ...
- Redis 中的原子操作(3)-使用Redis实现分布式锁
Redis 中的分布式锁如何使用 分布式锁的使用场景 使用 Redis 来实现分布式锁 使用 set key value px milliseconds nx 实现 SETNX+Lua 实现 使用 R ...
随机推荐
- SOJ 1685:chopsticks(dp)
题目链接 说实话挺喜欢soj的界面,简简单单,没有多余的东西hhh(但是简单到连内存限制,时间限制都看不到了. 题意是有个“奇葩”的主人公,吃饭要用三根筷子.两根短的一根长的. 现在给你n根筷子,要在 ...
- React Router 4.0 ---- 嵌套路由和动态路由
嵌套路由,从广义上来说,分为两种情况:一种是每个路由到的组件都有共有的内容,这时把共有的内容抽离成一个组件,变化的内容也是一个组件,两种组件组合嵌套,形成一个新的组件.另一种是子路由,路由到的组件内部 ...
- 本科理工男如何学习Linux
我是一个本科学电子的理工男,但是一直对计算机感兴趣,所以平时自己在课下喜欢学一些与计算机有关的东西.由于对计算机感兴趣,所以后来我参加了学校的计算机社团,在那里接受一些培训和指导.当时在社团里看到师兄 ...
- 【嵌入式】Arduino编程基础到应用全解析
Arduino Author: Andrew.Du 基础 基础语法: setup() loop() pinMode(引脚,模式) pinMode(13,OUTPUT):设置13号引脚为输出 //在使用 ...
- 彻底弄懂 HTTP 缓存机制及原理 | 干货
来源:www.cnblogs.com/chenqf/p/6386163.html 前言 Http 缓存机制作为 web 性能优化的重要手段,对于从事 Web 开发的同学们来说,应该是知识体系库中的一个 ...
- 部署crm项目
准备工作 使用xftp将项目传到linux 将knight 传到linux上 将项目的数据导出 mysqldum -uroot -p --all-database > alldb.dump 在w ...
- Jquery实现检测用户输入用户名和密码不能为空
要求 1.用户名和密码为空点击登录时提示相应的提示 2.获取用户名输入框时,错误提示清除 思路 1.创建1个input-text标签和1个input-password标签,1个input-botton ...
- Civil 3d设置横断面图样式
一位网友提出这样一个问题: 在使用SectionView.StyleName属性时, 会抛出异常:need to override property StyleName. 我测试的结果一样, 同时测试 ...
- Spring 使用介绍(三)—— 资源
一.Resource接口 Spring提供Resource接口,代表底层外部资源,提供对底层外部资源的一致性访问接口 public interface InputStreamSource { Inpu ...
- linux shell系列10 判断某个月中的星期六和星期天
#!/bin/bashread -p "请输入月份:" month #输入要查找的月份 mon=`date -d "0 month ago" +%m` #计算本 ...