一个简单的(基于redisson的)分布式同步工具类封装
一个简单的(基于redisson的)分布式同步工具类封装
- 一个简单的(基于redisson的)分布式同步工具类封装
- 背景说明
- 准备工作
- 第一步:引入redisson依赖
- 第二步:配置RedissonClient
- 工具类
- 工具类接口
- 工具类接口的默认实现
- 工具类接口涉及到的两个其它接口
- 使用示例
背景说明
有些分布式同步逻辑不需要作用于整个方法,只需要作用于指定的业务逻辑代码块即可,类似于synchronized代码块。于是有了下面这个简单的封装类。
准备工作
提示:此同步工具类中的redis分布式锁直接采用redisson实现。
第一步:引入redisson依赖
<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.17.0</version>
</dependency>
第二步:配置RedissonClient
提示:这里的配置以单体redis为例,更多配置详见redisson官网。
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** RedissonConfig** @author JustryDeng* @since 2022/3/25 10:33*/
@Slf4j
@Configuration
public class RedissonConfig {@Value("${spring.redis.host}")private String host;@Value("${spring.redis.port}")private String port;@Beanpublic RedissonClient redissonClient() {Config config = new Config();String address = host + ":" + port;log.info("redis address -> {}", address);config.useSingleServer().setAddress("redis://" + address);RedissonClient redissonClient = Redisson.create(config);return redissonClient;}
}
工具类
工具类接口
import lombok.Getter;import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;/*** redis锁支持** @author JustryDeng* @since 2022/4/19 9:36*/
public interface RedisLockSupport {/*** 执行同步逻辑* <br />* 此逻辑,应由redis lock保证全局同步** @param function* 业务逻辑块* @param param* 参数** @throws NotAcquiredRedisLockException 获取redis锁时抛出失败* @return 逻辑执行结果*/<P, R> R exec(Function<P, R> function, P param) throws NotAcquiredRedisLockException;/*** 执行同步逻辑* <br />* 此逻辑,应由redis lock保证全局同步** @param function* 业务逻辑块* @return 执行结果** @throws NotAcquiredRedisLockException 获取redis锁时抛出失败*/<R> R exec(NoArgFunction<R> function) throws NotAcquiredRedisLockException;/*** 执行同步逻辑* <br />* 此逻辑,应由redis lock保证全局同步** @param consumer* 业务逻辑块* @param param* 参数** @throws NotAcquiredRedisLockException 获取redis锁时抛出失败*/<P> void voidExec(Consumer<P> consumer, P param) throws NotAcquiredRedisLockException;/*** 执行同步逻辑* <br />* 此逻辑,应由redis lock保证全局同步** @param consumer* 业务逻辑块* @throws NotAcquiredRedisLockException 获取redis锁时抛出失败*/void voidExec(NoArgConsumer consumer) throws NotAcquiredRedisLockException;/*** 获取redis lock失败** @author JustryDeng* @since 2022/4/19 10:44*/@Getterclass NotAcquiredRedisLockException extends RuntimeException{/** 锁 key */private final String lockKey;/** 等待获取锁的最大时长 */private final long waitTime;/** waitTime的时间单位 */private final TimeUnit timeUnit;public NotAcquiredRedisLockException(String lockKey, long waitTime, TimeUnit timeUnit) {super(String.format("lockKey=%s, waitTime=%d, timeUnit=%s", lockKey, waitTime, timeUnit));this.lockKey = lockKey;this.waitTime = waitTime;this.timeUnit = timeUnit;}}}
工具类接口的默认实现
import lombok.Getter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;/*** redis分布式锁默认实现* <p>* 使用示例见{@link com.ideaaedi.heywebuy.srv.RedisLockSupportTest}* </p>** @author JustryDeng* @since 2022/4/19 10:08*/
@Getter
public class DefaultRedisLockSupport implements RedisLockSupport {/** 默认的redisson客户端 */private static volatile RedissonClient defaultRedissonClient;/** redisson客户端(优先级高于defaultRedissonClient,当redissonClient不为null时,使用redissonClient) */protected RedissonClient redissonClient;/** 锁 key */protected final String lockKey;/** 等待获取锁的最大时长 */protected long waitTime = 1L;/** 释放锁的最大时长 */protected long leaseTime = 3L;/** WaitTime和LeaseTime的时间单位 */protected TimeUnit unit = TimeUnit.SECONDS;public DefaultRedisLockSupport(String lockKey) {this.lockKey = lockKey;}public DefaultRedisLockSupport(RedissonClient redissonClient, String lockKey) {this.redissonClient = redissonClient;this.lockKey = lockKey;}public DefaultRedisLockSupport(String lockKey, long waitTime, long leaseTime) {this.lockKey = lockKey;this.waitTime = waitTime;this.leaseTime = leaseTime;}public DefaultRedisLockSupport(RedissonClient redissonClient, String lockKey, long waitTime, long leaseTime) {this.redissonClient = redissonClient;this.lockKey = lockKey;this.waitTime = waitTime;this.leaseTime = leaseTime;}public DefaultRedisLockSupport(String lockKey, long waitTime, long leaseTime, TimeUnit unit) {this.lockKey = lockKey;this.waitTime = waitTime;this.leaseTime = leaseTime;this.unit = unit;}public DefaultRedisLockSupport(RedissonClient redissonClient, String lockKey, long waitTime, long leaseTime, TimeUnit unit) {this.redissonClient = redissonClient;this.lockKey = lockKey;this.waitTime = waitTime;this.leaseTime = leaseTime;this.unit = unit;}@Overridepublic <P, R> R exec(Function<P, R> function, P param) throws NotAcquiredRedisLockException {RedissonClient client = redissonClient();RLock lock = client.getLock(lockKey);boolean obtainLock = false;try {obtainLock = lock.tryLock(waitTime, leaseTime, unit);} catch (InterruptedException e) {// ignore}if (obtainLock) {try {return function.apply(param);} finally {if (lock.isHeldByCurrentThread()) {lock.unlock();}}}throw new NotAcquiredRedisLockException(lockKey, waitTime, unit);}@Overridepublic <R> R exec(NoArgFunction<R> function) throws NotAcquiredRedisLockException {RedissonClient client = redissonClient();RLock lock = client.getLock(lockKey);boolean obtainLock = false;try {obtainLock = lock.tryLock(waitTime, leaseTime, unit);} catch (InterruptedException e) {// ignore}if (obtainLock) {try {return function.apply();} finally {if (lock.isHeldByCurrentThread()) {lock.unlock();}}}throw new NotAcquiredRedisLockException(lockKey, waitTime, unit);}@Overridepublic <P> void voidExec(Consumer<P> consumer, P param) throws NotAcquiredRedisLockException {RedissonClient client = redissonClient();RLock lock = client.getLock(lockKey);boolean obtainLock = false;try {obtainLock = lock.tryLock(waitTime, leaseTime, unit);} catch (InterruptedException e) {// ignore}if (obtainLock) {try {consumer.accept(param);return;} finally {if (lock.isHeldByCurrentThread()) {lock.unlock();}}}throw new NotAcquiredRedisLockException(lockKey, waitTime, unit);}@Overridepublic void voidExec(NoArgConsumer consumer) throws NotAcquiredRedisLockException {RedissonClient client = redissonClient();RLock lock = client.getLock(lockKey);boolean obtainLock = false;try {obtainLock = lock.tryLock(waitTime, leaseTime, unit);} catch (InterruptedException e) {// ignore}if (obtainLock) {try {consumer.accept();return;} finally {if (lock.isHeldByCurrentThread()) {lock.unlock();}}}throw new NotAcquiredRedisLockException(lockKey, waitTime, unit);}/*** 获取RedissonClient实例** @return RedissonClient实例*/protected RedissonClient redissonClient() {if (this.redissonClient != null) {return this.redissonClient;}if (DefaultRedisLockSupport.defaultRedissonClient != null) {return DefaultRedisLockSupport.defaultRedissonClient;}throw new IllegalStateException("There is not redissonClient available.");}/*** 初始化默认的Redisson客户端** @param redissonClient* Redisson客户端实例*/public static void initDefaultRedissonClient(RedissonClient redissonClient) {if (DefaultRedisLockSupport.defaultRedissonClient != null && !DefaultRedisLockSupport.defaultRedissonClient.equals(redissonClient)) {throw new IllegalStateException("defaultRedissonClient already been initialized.");}synchronized (DefaultRedisLockSupport.class) {if (DefaultRedisLockSupport.defaultRedissonClient != null) {if (DefaultRedisLockSupport.defaultRedissonClient.equals(redissonClient)) {return;}throw new IllegalStateException("defaultRedissonClient already been initialized.");}DefaultRedisLockSupport.defaultRedissonClient = redissonClient;}}
}
工具类接口涉及到的两个其它接口
NoArgConsumer
/*** 无参 Consumer** @author JustryDeng* @since 2022/4/19 11:17*/ @FunctionalInterface public interface NoArgConsumer {/*** 执行逻辑*/void accept(); }
NoArgFunction
/*** 无参 Function** @author JustryDeng* @since 2022/4/19 11:17*/ @FunctionalInterface public interface NoArgFunction<R> {/*** 执行逻辑** @return 执行结果*/R apply(); }
使用示例
import com.ideaaedi.heywebuy.srv.config.redisson.DefaultRedisLockSupport;
import com.ideaaedi.heywebuy.srv.config.redisson.NoArgConsumer;
import com.ideaaedi.heywebuy.srv.config.redisson.NoArgFunction;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.redisson.api.RedissonClient;
import org.springframework.boot.test.context.SpringBootTest;import javax.annotation.Resource;
import java.util.Collections;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;@Slf4j
@SpringBootTest
public class RedisLockSupportTest {@Resourceprivate RedissonClient redissonClient;@BeforeEachvoid initMethod() {// 在项目启动时注入DefaultRedisLockSupport.initDefaultRedissonClient(redissonClient);}@Testvoid test() {// 无参数 无返回值 NoArgConsumernew DefaultRedisLockSupport("DefaultRedisLockSupport1", 1, 120).voidExec(new NoArgConsumer() {@Overridepublic void accept() {// 我是业务逻辑System.out.println("[无参数 无返回值 NoArgConsumer]:\t\t\t\t\t" + 111111);}});// 有参数 无返回值 Consumernew DefaultRedisLockSupport("DefaultRedisLockSupport1", 1, 120).voidExec(new Consumer<Integer>() {@Overridepublic void accept(Integer s) {// 我是业务逻辑System.out.println("[有参数 无返回值 Consumer]:\t\t\t\t\t" + s);}}, 2222);// 无参数 有返回值 NoArgFunctionMap<String, Object> result = new DefaultRedisLockSupport("DefaultRedisLockSupport1", 1, 120).exec(new NoArgFunction<Map<String, Object>>() {@Overridepublic Map<String, Object> apply() {// 我是业务逻辑return Collections.singletonMap("k1", "v1");}} );System.out.println("[无参数 有返回值 Function]:\t\t\t\t\t" + result);// 有参数 有返回值 Functionresult = new DefaultRedisLockSupport("DefaultRedisLockSupport1", 1, 120).exec(new Function<String,Map<String, Object>>() {@Overridepublic Map<String, Object> apply(String s) {// 我是业务逻辑return Collections.singletonMap("k2", s);}}, "v2");System.out.println("[有参数 有返回值 Function]:\t\t\t\t\t" + result);}
}
相关资料
- 本文已被收录进《程序员成长笔记》 ,笔者JustryDeng
一个简单的(基于redisson的)分布式同步工具类封装相关推荐
- android sqlite 操作类封装,SQLiteUtils 一个简单的基于 Android 的 Sqlite 数据库的操作封装库 @codeKK Android开源站...
一个简单的基于 Android 的 Sqlite 数据库的操作封装,它有如下的好处: 便捷地创建表和增添表字段 通过操作对象来 insert 或者 update 表记录 支持多种查询方式,支持分页查询 ...
- java 分布式任务_一个简单的基于 Redis 的分布式任务调度器 —— Java 语言实现...
折腾了一周的 Java Quartz 集群任务调度,很遗憾没能搞定,网上的相关文章也少得可怜,在多节点(多进程)环境下 Quartz 似乎无法动态增减任务,恼火.无奈之下自己撸了一个简单的任务调度器, ...
- 通过Dapr实现一个简单的基于.net的微服务电商系统(十九)——分布式事务之Saga模式...
目录: 一.通过Dapr实现一个简单的基于.net的微服务电商系统 二.通过Dapr实现一个简单的基于.net的微服务电商系统(二)--通讯框架讲解 三.通过Dapr实现一个简单的基于.net的微服务 ...
- 通过Dapr实现一个简单的基于.net的微服务电商系统(六)——一步一步教你如何撸Dapr之Actor服务...
我个人认为Actor应该是Dapr里比较重头的部分也是Dapr一直在讲的所谓"stateful applications"真正具体的一个实现(个人认为),上一章讲到有状态服务可能很 ...
- java设计前期工作基础和存在的困难_Java秒杀系统实战系列-基于Redisson的分布式锁优化秒杀逻辑...
本文是"Java秒杀系统实战系列文章"的第十五篇,本文我们将借助综合中间件Redisson优化"秒杀系统中秒杀的核心业务逻辑",解决Redis的原子操作在优化秒 ...
- 通过Dapr实现一个简单的基于.net的微服务电商系统(十六)——dapr+sentinel中间件实现服务保护...
dapr目前更新到了1.2版本,在之前4月份的时候来自阿里的开发工程师发起了一个dapr集成Alibaba Sentinel的提案,很快被社区加入到了1.2的里程碑中并且在1.2 release 相关 ...
- 通过Dapr实现一个简单的基于.net的微服务电商系统(十二)——istio+dapr构建多运行时服务网格...
多运行时是一个非常新的概念.在 2020 年,Bilgin Ibryam 提出了 Multi-Runtime(多运行时)的理念,对基于 Sidecar 模式的各种产品形态进行了实践总结和理论升华.那到 ...
- 通过Dapr实现一个简单的基于.net的微服务电商系统(八)——一步一步教你如何撸Dapr之链路追踪
Dapr提供了一些开箱即用的分布式链路追踪解决方案,今天我们来讲一讲如何通过dapr的configuration来实现非侵入式链路追踪的 目录: 一.通过Dapr实现一个简单的基于.net的微服务电商 ...
- 通过Dapr实现一个简单的基于.net的微服务电商系统(七)——一步一步教你如何撸Dapr之服务限流...
在一般的互联网应用中限流是一个比较常见的场景,也有很多常见的方式可以实现对应用的限流比如通过令牌桶通过滑动窗口等等方式都可以实现,也可以在整个请求流程中进行限流比如客户端限流就是在客户端通过随机数直接 ...
- 通过Dapr实现一个简单的基于.net的微服务电商系统(五)——一步一步教你如何撸Dapr之状态管理...
状态管理和上一章的订阅发布都算是Dapr相较于其他服务网格框架来讲提供的比较特异性的内容,今天我们来讲讲状态管理. 目录: 一.通过Dapr实现一个简单的基于.net的微服务电商系统 二.通过Dapr ...
最新文章
- “人在旅途”之随想以及旅游指南(travel.msra.cn)简介
- 从0开始利用宝塔linux面板+WordPress一键部署搭建个人的博客介绍
- VTK:Utilities之DetermineActorType
- Exception in thread main expected 'document start', but found BlockMappingStart in 'reader'(测试了)
- python 数据类型之间的转换
- oracle 存储过程 db,oracle数据库的存储过程是什么?
- 数据结构与算法(刺猬书)读书笔记----目录
- c++ 按行读取txt文件并赋值_python操作txt文件中数据教程[3]python读取文件夹中所有txt文件并将数据转为csv文件...
- python卡方检验计算pvalue值_如何用python计算临界值(critical value)和p值(p value)(scipy)...
- C语言 将一个字符串转换为字符,每两个字符间用空格隔开
- vue表单校验,根据某选项追加或去掉校验
- 数据增强方法:图片镜像、图片缩放、图片旋转、加噪点
- 数值分析实验 实验3-1 牛顿下山法 python3实现
- JZ·7.8.2019
- 如何学计算机打字,如何学习最快的电脑打字如何为电脑新手快速学习打字
- Aria2高速下载利器 带你冲破百度网盘重重束缚
- 什么是桶(bucket)?什么是度量(metrics)?
- 火箭军下连学计算机专业好吗,火箭军招收定向培养士官院校增至16所 涵盖18个专业...
- Java基础相关6(IO)
- 【励志】如何有技巧地自律
热门文章
- 标准盒子模型简单讲解
- html二级菜单点击淡入淡出,Web前端开发实战1:二级下拉式菜单之CSS实现
- js批量生成条形码制作前端标签打印工具
- 计算机网络维护服务承诺书,网络信息技术中心服务承诺书
- 在计算机中360云盘如何删除文件,如何在360云盘中检索已删除的文件
- 农林牧渔行业S2B2C系统网站提升品牌知名度,提升盈利水平
- OpenGL+VS2015相关类库配置
- delphi技巧总结收集
- 方正飞鸿智能信息平台(FIX ES2007)帮助手册+知识库
- 【小月电子】ALTERA FPGA开发板系统学习教程-LESSON8 LCD1602液晶显示