一、组件概述
该分布式幂等组件基于 Spring AOP 和 Redis 实现,通过注解声明式配置,提供灵活、高性能的接口幂等性保障。支持快速失败与阻塞等待两种模式,集成自定义策略扩展能力,适用于springboot项目分布式场景下的重复请求拦截与处理。
代码已上传github,欢迎star: 传送门
二、核心功能点
- 声明式注解支持
通过@Idempotent
注解标记需幂等控制的方法,支持参数动态配置。 - 多种策略组合
- 幂等策略:默认基于 Redis 缓存,支持自定义处理器(如数据库持久化)。
- 响应策略 :支持返回空值(
NULL
)、抛出异常(EXCEPTION
)、返回历史结果(PREVIOUS
)。
- 并发控制
基于 Redisson 分布式锁实现,支持快速失败(failFast=true
)或阻塞等待锁(failFast=false
)。 - 动态参数识别
通过columns
参数指定幂等键生成规则,支持 EL 表达式动态提取参数。 - 自动配置与扩展
通过 Spring Boot starter 自动装配,支持自定义处理器工厂扩展,无缝集成现有系统。
三、代码逻辑流程
1. 请求拦截与上下文构建
- AOP 切面拦截 :通过
IdempotentAspect
拦截被@Idempotent
注解标记的方法。 - 生成唯一标识 :根据
uid
或类名+方法名
构建唯一键,结合参数生成 Redis Key。 - 上下文初始化 :收集参数、策略配置、处理器实例,封装为
IdempotentContext
。
核心代码:
java
@Around("@annotation(anno)")
public final Object proceed(ProceedingJoinPoint joinPoint, Idempotent anno) throws Throwable {
// 所有的参数值的数组
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 幂等增强 委托给IdempotentExecutor
Object retObj = executor.execute(new IdempotentCallback<Object>() {
@Override
public IdempotentContext<Object> initContext() throws Throwable {
return new IdempotentContext<>()
.setUid(buildUid(joinPoint, signature, anno))
.setDuration(anno.duration())
.setArgMap(buildArgMap(joinPoint, signature))
.setIdemParam(buildIdemParam(joinPoint, anno))
.setFailFast(anno.failFast())
.setIdemStrategy(anno.custom().idemStrategy())
.setIdemHandler(IdemHandlerUtils.getIdemHandler(joinPoint, anno.custom()))
.setRespStrategy(anno.custom().respStrategy())
.setRespHandler(IdemHandlerUtils.getRespHandler(joinPoint, anno.custom()));
}
@Override
public Object execute() throws Throwable {
return joinPoint.proceed();
}
});
return JSON.parseObject(JSON.toJSONString(retObj), (Class<?>) signature.getReturnType());
}
2. 幂等校验与执行
- 分布式锁控制 :通过
IdempotentExecutor
获取 Redisson 锁,确保并发安全。- 快速失败模式:尝试获取锁,失败则直接触发响应策略。
- 阻塞模式:等待锁释放后执行逻辑。
- 持久化校验 :调用
PersistProcessor
检查 Redis 中是否存在历史记录,避免重复提交。 - 业务逻辑执行:若校验通过,执行业务代码并将结果持久化到 Redis。
3. 结果处理
- 自定义响应:根据策略返回预定义结果(如异常、空值或历史数据)。
- 自动清理:通过 Redis 的 TTL 机制自动清理过期记录。
核心代码:
java
public final <R> R execute(IdempotentCallback<R> callback) throws Throwable {
IdempotentContext<R> context = callback.initContext();
String uid = context.getUid();
if (StringUtils.isBlank(uid)) {
throw new Exception("幂等组件: uid不能为空");
}
List<Object> param = context.getIdemParam();
if (CollectionUtils.isEmpty(param)) {
throw new Exception("幂等组件: idemParam不能为空");
}
RLock lock = client.getLock(buildLock(uid, param));
return context.isFailFast() ? doFast(callback, context, lock) : doLazy(callback, context, lock);
}
private <R> R doLazy(IdempotentCallback<R> callback, IdempotentContext<R> context, RLock lock) throws Throwable {
lock.lock();
try {
IdempotentResult<R> result = context.getIdemHandler().handle(context);
if (result.isFlag()) {
//执行
R r = callback.execute();
//缓存结果,自定义策略不缓存
processor.persist(context.setResult(r));
return r;
}
return context.setResult(result.getResult()).getRespHandler().handle(context);
} finally {
lock.unlock();
}
}
private <R> R doFast(IdempotentCallback<R> callback, IdempotentContext<R> context, RLock lock) throws Throwable {
if (lock.tryLock()) {
try {
//获取到锁,执行逻辑
IdempotentResult<R> result = context.getIdemHandler().handle(context);
if (result.isFlag()) {
//执行
R r = callback.execute();
//缓存结果,自定义策略不缓存
processor.persist(context.setResult(r));
return r;
}
} finally {
lock.unlock();
}
}
return context.getRespHandler().handle(context);
}
四、使用方式
1. 添加依赖(pom.xml)
xml
<dependency>
<groupId>com.shemuel</groupId>
<artifactId>idempotent-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
2. 配置文件
yaml
idempotent:
enable: true
env: prod
redis:
host: 127.0.0.1
port: 6379
password: redis-pass
3. 添加注解
java
@Idempotent(
uid = "order.create",
columns = {"#userId", "#orderId"}, // 动态提取参数
failFast = true,
duration = 10,
custom = @Strategy(
idemStrategy = IdemStrategy.DEFAULT,
respStrategy = RespStrategy.EXCEPTION
)
)
public Order createOrder(Long userId, String orderId) {
// 业务逻辑
}
4. 自定义扩展(按需)
- 实现
IdemHandler
:覆盖doHandle
方法定义自定义幂等逻辑。 - 实现
RespHandler
:覆盖doHandle
方法定义自定义响应逻辑。 - 工厂注册:通过
IdemHandlerFactory
或RespHandlerFactory
注册 Bean。
五、设计模式应用
- 模板方法模式
IdempotentExecutor
封装执行流程,子流程(如锁获取、持久化)通过回调接口IdempotentCallback
实现。 - 策略模式
通过IdemStrategy
和RespStrategy
定义多套处理策略,支持运行时动态切换。 - 工厂模式
IdemHandlerFactory
和RespHandlerFactory
集中管理处理器实例,解耦创建与使用逻辑。 - 代理模式
Spring AOP 动态代理拦截方法调用,实现非侵入式幂等控制。 - 单例模式
Spring Bean 默认单例,确保处理器和配置类的全局唯一性。
六、总结
该组件通过 注解驱动 + 分布式锁 + 策略扩展 的设计,实现了轻量级、高可用的幂等控制。开发者无需修改业务代码即可集成,同时支持灵活扩展,适用于电商、金融等高并发场景,有效保障系统数据一致性。
代码已上传github, 欢迎star; github.com/ShemuelDeng...
最后
欢迎关注gzh:加瓦点灯, 每天推送干货知识!