jedis 与 redission 实现分布式锁
本文为博主原创,未经允许不得转载:
目录:
1. Jedis 实现分布式锁
2. Redission 实现分布式锁
为了确保分布式锁可用,至少要保证锁的实现同时满足以下几个条件
- 互斥性:在任意时刻只有一个客户端能持有锁
- 不会死锁:即使有一个客户端在持有锁的期间发生崩溃而没有主动解锁,也能保证后续其它客户端能加锁
- 容错性:只要大部分的Redis节点正常运行,客户端就可以加锁和解锁
- 解铃还须系铃人:加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解除
1. Jedis 实现分布式锁
1.1 引入 jedis 依赖:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
1.2 Jedis 封装工具类,封装分布式锁及解锁方法
package com.example.demo.util;
import redis.clients.jedis.Jedis;
import java.util.Collections;
public class JedisUtils {
private static final String LOCK_SUCCESS = "OK";
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";
private static final Long RELEASE_SUCCESS = 1L;
/**
* 尝试获取分布式锁
* @param jedis Redis客户端
* @param lockKey 锁
* @param requestId 请求标识
* @param expireTime 超期时间
* @return 是否获取成功
*/
public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if (LOCK_SUCCESS.equals(result)) {
return true;
}
return false;
}
/**
* 释放分布式锁
* @param jedis Redis客户端
* @param lockKey 锁
* @param requestId 请求标识
* @return 是否释放成功
*/
public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
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(lockKey), Collections.singletonList(requestId));
if (RELEASE_SUCCESS.equals(result)) {
return true;
}
return false;
}
}
获取分布式锁使用的是 Jedis 内部封装的 set(String key, String value, String nxxx, String expx, int time) 方法。该方法可保证 redis 获取分布式锁操作的原子性。在网上会看到通过 jedis 的 setnx (lockKey, requestId) 与 jedis.expire(lockKey, expireTime) 两步来获取分布式锁,这种将获取分布式锁 分为两步或三步的,并添加逻辑校验的,往往看起来并没什么问题,但这种破坏了原子性,在高并发场景性,会存在丢锁的场景。建议使用以上的方式 获取分布式锁。同理分布式锁解锁也一样,而 lua 脚本具有天然的原子性,可以保证执行的安全性。
2. Redission 实现分布式锁
redisson简单易用、支持锁重入、支持阻塞等待、Lua脚本原子操作等,内部封装了很多redis 的解决方案,使用reids 客户端时,优先推荐使用 redission 客户端。
2.1 引入依赖:
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.8.2</version>
</dependency>
2.2 定义 redisision 客户端配置
package com.example.demo.config; import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; @Configuration
public class RedissonConfig {
@Bean
public RedissonClient getClient() {
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);
return redisson;
} }
2.3 常用方法:
package com.example.demo.util; import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service; import java.util.concurrent.TimeUnit; @Service
public class RedissionUtils { private RedissonClient redissonClient; public void test(){
RLock lock = redissonClient.getLock("lockName");
try{
// 1. 最常见的使用方法
lock.lock();
boolean lockResult = lock.tryLock();
// 2. 支持过期解锁功能,10秒钟以后自动解锁, 无需调用unlock方法手动解锁
//lock.lock(10, TimeUnit.SECONDS);
// 3. 尝试加锁,最多等待2秒,上锁以后8秒自动解锁
boolean res = lock.tryLock(2, 8, TimeUnit.SECONDS);
if(res){ //成功
//处理业务
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放锁
lock.unlock();
}
} }
方法说明:
lock.lock(); 与 boolean lockResult = lock.tryLock(); 方法均为获取分布式锁,前面方法无返回值,后面方法返回值为 boolean 类型。该方法获取分布式锁会自动续锁,即通过redission 内部封装的看门狗进行任务续时,jedis 分布式锁不支持任务续时,如果在锁时间内,任务尚未执行完,则会丢锁。
lock.lock(); 与 lock.tryLock(); 方法如果带有入参,则不会执行看门狗模式,即不会分布式锁续时。
boolean tryLock(long waitTime, long leaseTime, TimeUnit var5) throws InterruptedException; 获取分布式最多等待时间waitTime,分布式锁过期时间leaseTime。
续时任务执行的源码中可看到看门狗模式的执行,代码如下:
private RFuture<Boolean> tryAcquireOnceAsync(long leaseTime, TimeUnit unit, final long threadId) {
if (leaseTime != -1L) {
// 传参
return this.tryLockInnerAsync(leaseTime, unit, threadId, RedisCommands.EVAL_NULL_BOOLEAN);
} else {
// 不传参。 getLockWatchdogTimeout ,看门狗监听超时,如果超时则续时
RFuture<Boolean> ttlRemainingFuture = this.tryLockInnerAsync(this.commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_NULL_BOOLEAN); ttlRemainingFuture.addListener(new FutureListener<Boolean>() { public void operationComplete(Future<Boolean> future) throws Exception { if (future.isSuccess()) { Boolean ttlRemaining = (Boolean)future.getNow(); if (ttlRemaining) { RedissonLock.this.scheduleExpirationRenewal(threadId); } } } }); return ttlRemainingFuture; } }
lock.unlock(); 释放分布式锁。
3. redis 分布式锁优化思考:
每秒上千订单场景下的分布式锁高并发优化实践!【石杉的架构笔记】:https://mp.weixin.qq.com/s/RLeujAj5rwZGNYMD0uLbrg
jedis 与 redission 实现分布式锁的更多相关文章
- Redis实现分布式锁的正确使用方式(java版本)
Redis实现分布式锁的正确使用方式(java版本) 本文使用第三方开源组件Jedis实现Redis客户端,且只考虑Redis服务端单机部署的场景. 分布式锁一般有三种实现方式: 1. 数据库乐观锁: ...
- Redis优雅实现分布式锁
文章原创于公众号:程序猿周先森.本平台不定时更新,喜欢我的文章,欢迎关注我的微信公众号. 在实际项目开发中经常会遇到这样一个业务场景:如果同一台机器有多个线程抢夺同一个共享资源,同一个线程多次执行会出 ...
- 分布式锁实现(一):Redis
前言 单机环境下我们可以通过JAVA的Synchronized和Lock来实现进程内部的锁,但是随着分布式应用和集群环境的出现,系统资源的竞争从单进程多线程的竞争变成了多进程的竞争,这时候就需要分布式 ...
- Redis分布式锁 (图解-秒懂-史上最全)
文章很长,而且持续更新,建议收藏起来,慢慢读! 高并发 发烧友社群:疯狂创客圈(总入口) 奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : 极致经典 + 社群大片好评 < Java 高并发 三 ...
- springboot+redis分布式锁-模拟抢单
本篇内容主要讲解的是redis分布式锁,这个在各大厂面试几乎都是必备的,下面结合模拟抢单的场景来使用她:本篇不涉及到的redis环境搭建,快速搭建个人测试环境,这里建议使用docker:本篇内容节点如 ...
- 从redis中取值如果不存在设置值,使用Redisson分布式锁【我】
用到的jar包: <!-- Redis客户端 --> <dependency> <groupId>redis.clients</groupId> < ...
- 基于Redisson实现分布式锁
前言 最近开发了几个微服务上线了,发现定时任务执行了很多次,查看rancher发现这几个微服务都是多实例的,也就是说定时任务执行了多次,恰好所用框架中使用的是Redisson, 正好记录下使用Redi ...
- Redis分布式锁实例
maven依赖 <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</ ...
- Redis的分布式锁
一.锁的作用 当多线程执行某一业务时(特别是对数据的更新.新增)等操作,可能就会出现多个线程对同一条数据进行修改.其最终的结果一定与你期望的结果“不太一样”,这就与需要一把锁来控制线程排排队了 - j ...
- 分布式锁的几种实现方法:redis实现分布式锁
使用失效的方式实现分布式锁(推荐) import redis.clients.jedis.Jedis; /** * 使用redis实现分布式锁(推荐) * */ public class JedLoc ...
随机推荐
- CSP_J
- 数字孪生和GIS结合将会为智慧园区带来怎样的改变?
数字孪生和地理信息系统(GIS)是当今智慧园区发展中的两个重要技术,它们的结合将为智慧园区带来根本性的改变和巨大的发展机遇.这种结合将深刻影响园区的规划.建设.运营和管理,为人们创造更智能.高效.可持 ...
- 【C#】【IO】【实例】统计多个文件夹下的图片
因工作需要繁琐的进行同一目录多个文件夹下的图片统计,便使用代码来解决. 需求:统计的是多少个文件夹包含了图片,并非是统计有多少张图. 我们先用Python来创建一个现场环境(巩固巩固py知识): 1 ...
- 反转数组、打印数组元素、加强(增强)for循环
package com.guoba.array; public class Demo04 { public static void main(String[] args) { int[] arr = ...
- 多项式(Poly)笔记
开头先扔板子:多项式板子们 定义 多项式(polynomial)是形如 \(P(x) = \sum \limits_{i = 0}^{n} a_i x ^ i\) 的代数表达式.其中 \(x\) 是一 ...
- IO流小结图片
- 微信小程序数组
常用函数 concat(): 连接两个或多个数组,返回连接后的新数组. 示例:const arr1 = [1, 2, 3]; const arr2 = [4, 5, 6]; const arr3 = ...
- 第六部分_Shell脚本流程控制语句
流程控制语句 关键词:选择(人生漫漫长路,我该何去何从) 1. 基本语法结构 ㈠ if结构 箴言1:只要正确,就要一直向前冲️ F:表示false,为假 T:表示true,为真 if [ condit ...
- TooKit助力开发者上云
本文分享自华为云社区<华为云Classroom赋能-TooKit助力开发者上云>,作者:华为云PaaS服务小智 . 对于资深程序员而言,IDE是必不可少的,它好比是剑客手中的宝剑,IDE帮 ...
- 源生创新 云享未来|GOTC全球开源技术峰会华为云云原生精彩时刻
摘要:GOTC 全球开源技术峰会在上海张江科学会堂成功举办. 本文分享自华为云社区<源生创新 云享未来|GOTC全球开源技术峰会华为云云原生精彩时刻>,作者:华为云云原生团队. GOTC ...