- 幂等性
- 什么场景下需要用到幂等
- 幂等的实现原理
-
幂等的代码实现
- 幂等的使用
幂等性
今天我们来谈谈什么是幂等性
?
引用百度百科
的解析如下:
❝
幂等(idempotent、idempotence)是一个数学与计算机学概念,常见于抽象代数中。
在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数
重复执行
,并能获得相同结果
的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。例如,“setTrue()”函数就是一个幂等函数,无论多次执行,其结果都是一样的.更复杂的操作幂等保证是利用唯一交易号(流水号)实现。❞
这解析,确实有点长
了,大家话看看就行了!!!(●'◡'●)
那对于我们程序员
来说,我们关心的更多是下面这些问题:
❝
什么地方,什么场景下需要用到幂等?
幂等,我们需要怎么做,如何实现幂等呢?
❞
基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
- 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
- 视频教程:https://doc.iocoder.cn/video/
什么场景下需要用到幂等
- 前端表单重复提交问题
- 用户订单支付问题
- 银行业务办理取号问题
- 用户恶意进行调接口问题
- 接口超时重复提交问题
- MQ消息进行重复消费
- ...
当然了,还有很多场景会用到幂等
,这里咱们就不一一列举出来了。
那我们要如何设计一个幂等功能呢,而且还是代码非侵入式
?
代码非侵入式
的意思,就是,我们的业务逻辑代码,不需要处理幂等
校验的逻辑。
业务功能不处理?那交给谁处理呢?别着急,听哥们一一道来。^_^
这里,要实现代码非侵入式
的幂等校验,我们就要使用到切面
编程了(@Aspect
)
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
幂等的实现原理
在系统中一些接口
需要增加幂等
处理,幂等
的概念是一个业务请求只能执行一次。类似银行业务办理,首先需要取一个号,然后用户使用这个号去柜台办理业务。这个号只能使用一次,如果过期或者已办理这个号就无效了。
我们的幂等
也是使用这种原理。
❝
- 1.首先客户端调用通过我们的系统获取一个号,我们称之为
幂等号
,这个号已经存在我们的系统中。- 2.客户端使用这个号,调用我们的接口。
- 3.我们系统判断这个号在我们的系统中已经存在,如果存在则允许业务办理,如果不存在,则表示这是一个非法的号,我们直接抛出异常。
- 4.当业务处理完成,我们会将这个号从我们的系统中删除掉。
❞
好了,这实现步骤
,也是十分清晰了呀!!!^_^
那么我们下面就来看代码如何实现了
幂等的代码实现
- 定义一个幂等处理接口
publicinterfaceIdempotence{
/**
*检查是否存在幂等号
*@paramidempotenceId幂等号
*@return是否存在
*/
booleancheck(StringidempotenceId);
/**
*记录幂等号
*@paramidempotenceId幂等号
*/
voidrecord(StringidempotenceId);
/**
*记录幂等号
*@paramidempotenceId幂等号
*@paramtime过期时间
*/
voidrecord(StringidempotenceId,Integertime);
/**
*删除幂等号
*@paramidempotenceId幂等号
*/
voiddelete(StringidempotenceId);
}
- 定义一个幂等处理接口实现类
@Component
publicclassRedisIdempotenceimplementsIdempotence{
@Autowired
privateRedisRepositoryredisRepository;
@Override
publicbooleancheck(StringidempotenceId){
returnredisRepository.exists(idempotenceId);
}
@Override
publicvoidrecord(StringidempotenceId){
redisRepository.set(idempotenceId,"1");
}
@Override
publicvoidrecord(StringidempotenceId,Integertime){
redisRepository.setExpire(idempotenceId,"1",time);
}
@Override
publicvoiddelete(StringidempotenceId){
redisRepository.del(idempotenceId);
}
}
❝
这个实现类,咱们就用
redis
存储这个幂等号
实现4个方法:检查是否存在幂等号
记录幂等号
记录幂等号(带过期时间)
删除幂等号
❞
- 幂等工具类
@Component
publicclassIdempotenceUtil{
@Autowired
privateRedisRepositoryredisRepository;
/**
*生成幂等号
*@return
*/
publicStringgenerateId(){
Stringuuid=UUID.randomUUID().toString();
StringuId=Base64Util.encode(uuid).toLowerCase();
redisRepository.setExpire(uId,"1",1800);
returnuId;
}
/**
*从Header里面获取幂等号
*@return
*/
publicStringgetHeaderIdempotenceId(){
ServletRequestAttributesattributes=(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequestrequest=attributes.getRequest();
StringidempotenceId=request.getHeader("idempotenceId");
returnidempotenceId;
}
}
❝
这个工具类,提供两个方法。
1.生成一个幂等号,咱们就用uuid
2.从Header里面获取幂等号
❞
- 定义一个注解
/**
*接口增加幂等性
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public@interfaceIdempotenceRequired{
}
- 切面
@Aspect
@Slf4j
@Component
publicclassIdempotenceSupportAdvice{
@Autowired
privateIdempotenceidempotence;
@Autowired
IdempotenceUtilidempotenceUtil;
/**
*拦截有@IdempotenceRequired注解的方法。
*/
@Pointcut("@annotation(xxx.xxx.IdempotenceRequired)")
publicvoididempotenceMethod(){}
@AfterThrowing(value="idempotenceMethod()()",throwing="e")
publicvoidafterThrowing(Throwablee){
if(!(einstanceofIdempotencyException)){
//从HTTPheader中获取幂等号idempotenceId
StringidempotenceId=idempotenceUtil.getHeaderIdempotenceId();
idempotence.record(idempotenceId,1800);
}
}
@Around(value="idempotenceMethod()")
publicObjectaround(ProceedingJoinPointjoinPoint)throwsThrowable{
//从HTTPheader中获取幂等号idempotenceId
StringidempotenceId=idempotenceUtil.getHeaderIdempotenceId();
if(StringUtils.isEmpty(idempotenceId)){
//不存在幂等号则不进行额外操作
returnjoinPoint.proceed();
}
//前置操作幂等号是否存在
booleanexisted=idempotence.check(idempotenceId);
if(!existed){
thrownewIdempotencyException("{success:false,message:"操作重复,请重新输入幂等号重试!",data:-2}");
}
//删除幂等号
idempotence.delete(idempotenceId);
Objectresult=joinPoint.proceed();
returnresult;
}
}
- 定义个controller
@RequestMapping("/idempotence")
publicclassIdempotenceController{
/**
*生成幂等号
*@return
*/
@GetMapping("/generateId")
publicJsonResultgenerateId(){
IdempotenceUtilidempotenceUtil=SpringUtil.getBean(IdempotenceUtil.class);
StringuId=idempotenceUtil.generateId();
returnJsonResult.success("成功生成!").setData(uId);
}
}
好了,实现的代码,就是这些了,理解
起来也是比较简单
,没有过多复杂的逻辑。
接下来,就是如何使用的问题
了,
这个使用,也是十分的简单啦!!!
幂等的使用
「服务端:」
不是所有的方法都需要切面拦截 ,只有 IdempotenceRequired
注解的方法才会被拦截。
例如下面接口:
@IdempotenceRequired
@PostMapping("/getUsers")
publicJsonResultgetUsers(){
//执行正常业务逻辑
...
}
在开发幂等
接口时,只需要在方法上简单增加一个 IdempotenceRequired
注解即可。
这基本上就是代码非侵入式
了呀!!!
「客户端:」
服务端处理好后,在客户端访问接口的时候需要执行以下步骤:
❝
- 需要先获取
幂等号
- 然后将
幂等号
添加到请求头中❞
-
1.获取
幂等号
http://服务地址/idempotence/generateIdhttp://xn--zfry9hnb732h/idempotence/generateId
- 2.请求调用
往header中添加幂等号❝
往header中添加幂等号
❞
好了,到这里幂等的实现,就已经完成了!!!^_^
那我们就可以愉快的编写代码了!!!^_^
审核编辑 :李倩
-
JAVA
+关注
关注
19文章
2954浏览量
104510 -
编程
+关注
关注
88文章
3578浏览量
93551 -
代码
+关注
关注
30文章
4733浏览量
68291
原文标题:推荐一种非侵入式幂等性的Java实现
文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论