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 ...
随机推荐
- 公私两用U盘管理软件:保障企业数据安全
随着信息技术的不断发展,企业对数据安全的关注程度越来越高.U盘作为一种便携式的移动存储设备,在工作中被广泛使用,但其本身也存在很多安全隐患,会面临数据泄漏和未经授权访问的风险.例如:企业研发部机密文档 ...
- 华企盾DSC启动服务器提示“发生系统错误5”
解决方法:没有管理员权限 导致,需要以管理员权限运行服务器安装包,覆盖安装一下
- jclasslib的安装
双击安装包打开后点击下一步,然后选择安装的路径. 默认路径,如果c盘有空间不建议更改路径,然后再次点击下一步 依然选择下一步 最后点击完成即可完成安装.现在可以使用啦 注:如果需要安装包,请点击下面链 ...
- DTD快速入门
DTD快速入门
- 从零玩转设计模式之外观模式-waiguanmos
title: 从零玩转设计模式之外观模式 date: 2022-12-12 15:49:05.322 updated: 2022-12-23 15:34:40.394 url: https://www ...
- Pikachu漏洞靶场 Sql Inject(SQL注入)
SQLi 哦,SQL注入漏洞,可怕的漏洞. 文章目录 SQLi 数字型注入(post) 字符型注入(get) 搜索型注入 xx型注入 "insert/update"注入 inser ...
- 揭秘Spring事务失效场景分析与解决方案
在Spring框架中,事务管理是一个核心功能,然而有时候会遇到事务失效的情况,这可能导致数据一致性问题.本文将深入探讨一些Spring事务失效的常见场景,并提供详细的例子以及解决方案. 1. 跨方法调 ...
- Java 中常见类型的判空方式
引用类型(Reference Types): 使用 == 运算符判断是否为 null. 使用 != 运算符判断是否不为 null. 使用 Objects.isNull() 方法判断是否为 null. ...
- 【云小课】版本管理发展史之Git+——代码托管
摘要:选择一款版本管理工具,已经被大多数企业作为项目的必要准备工作之一,相信没有一个开发者没有听过Git.SVN这些工具. 今天我们来寻根溯源,扒一扒版本管理的发展史. 版本管理工具之于软件开发,犹如 ...
- 单向数据流-从共享状态管理:flux/redux/vuex漫谈异步数据处理
不管是Vue,还是 React,都需要管理状态(state),比如组件之间都有共享状态的需要. 什么是共享状态? 比如一个组件需要使用另一个组件的状态,或者一个组件需要改变另一个组件的状态,都是共享状 ...