服务粉丝

我们一直在努力
当前位置:首页 > 财经 >

一种非侵入式幂等性的Java实现

日期: 来源:SpringForAll收集编辑:
关注我,回复关键字“spring”
免费领取Spring学习资料

作者:llsydn 链接:https://juejin.cn/post/7085944825085689864 

今天我们来谈谈什么是幂等性

引用百度百科的解析如下:

幂等(idempotent、idempotence)是一个数学与计算机学概念,常见于抽象代数中。

在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。例如,“setTrue()”函数就是一个幂等函数,无论多次执行,其结果都是一样的.更复杂的操作幂等保证是利用唯一交易号(流水号)实现。

这解析,确实有点了,大家话看看就行了!!!(●'◡'●)

那对于我们程序员来说,我们关心的更多是下面这些问题:

什么地方,什么场景下需要用到幂等?

幂等,我们需要怎么做,如何实现幂等呢?

image.png

什么场景下需要用到幂等

  • 前端表单重复提交问题
  • 用户订单支付问题
  • 银行业务办理取号问题
  • 用户恶意进行调接口问题
  • 接口超时重复提交问题
  • MQ消息进行重复消费
  • ...

当然了,还有很多场景会用到幂等,这里咱们就不一一列举出来了。

那我们要如何设计一个幂等功能呢,而且还是代码非侵入式

代码非侵入式的意思,就是,我们的业务逻辑代码,不需要处理幂等校验的逻辑。

业务功能不处理?那交给谁处理呢?别着急,听哥们一一道来。^_^

image.png

这里,要实现代码非侵入式的幂等校验,我们就要使用到切面编程了(@Aspect

幂等的实现原理

在系统中一些接口需要增加幂等处理,幂等的概念是一个业务请求只能执行一次。类似银行业务办理,首先需要取一个号,然后用户使用这个号去柜台办理业务。这个号只能使用一次,如果过期或者已办理这个号就无效了。

我们的幂等也是使用这种原理。

  • 1.首先客户端调用通过我们的系统获取一个号,我们称之为幂等号,这个号已经存在我们的系统中。
  • 2.客户端使用这个号,调用我们的接口。
  • 3.我们系统判断这个号在我们的系统中已经存在,如果存在则允许业务办理,如果不存在,则表示这是一个非法的号,我们直接抛出异常。
  • 4.当业务处理完成,我们会将这个号从我们的系统中删除掉。

好了,这实现步骤,也是十分清晰了呀!!!^_^

那么我们下面就来看代码如何实现了

幂等的代码实现

  • 定义一个幂等处理接口
public interface Idempotence {
/**
* 检查是否存在幂等号
* @param idempotenceId 幂等号
* @return 是否存在
*/
boolean check(String idempotenceId);

/**
* 记录幂等号
* @param idempotenceId 幂等号
*/
void record(String idempotenceId);

/**
* 记录幂等号
* @param idempotenceId 幂等号
* @param time 过期时间
*/
void record(String idempotenceId, Integer time);

/**
* 删除幂等号
* @param idempotenceId 幂等号
*/
void delete(String idempotenceId);

}
复制代码
  • 定义一个幂等处理接口实现类
@Component
public class RedisIdempotence implements Idempotence {
    @Autowired
    private RedisRepository redisRepository;

    @Override
    public boolean check(String idempotenceId) {
        return redisRepository.exists(idempotenceId);
    }

    @Override
    public void record(String idempotenceId) {
        redisRepository.set(idempotenceId,"1");
    }

    @Override
    public void record(String idempotenceId,Integer time) {
        redisRepository.setExpire(idempotenceId,"1",time);
    }

    @Override
    public void delete(String idempotenceId) {
        redisRepository.del(idempotenceId);
    }
}
复制代码

这个实现类,咱们就用redis存储这个幂等号 实现4个方法:

检查是否存在幂等号

记录幂等号

记录幂等号(带过期时间)

删除幂等号

  • 幂等工具类
@Component
public class IdempotenceUtil {
    @Autowired
    private RedisRepository redisRepository;
    /**
     * 生成幂等号
     * @return
     */
    public String generateId() {
        String uuid = UUID.randomUUID().toString();
        String uId=Base64Util.encode(uuid).toLowerCase();
        redisRepository.setExpire(uId,"1",1800);
        return uId;
    }

    /**
     * 从Header里面获取幂等号
     * @return
     */
    public String getHeaderIdempotenceId(){
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String idempotenceId=request.getHeader("idempotenceId");
        return idempotenceId;
    }
}
复制代码

这个工具类,提供两个方法。

1.生成一个幂等号,咱们就用uuid

2.从Header里面获取幂等号

  • 定义一个注解
/**
* 接口增加幂等性
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IdempotenceRequired {

}
复制代码
  • 切面
@Aspect
@Slf4j
@Component
public class IdempotenceSupportAdvice {
@Autowired
private Idempotence idempotence;
@Autowired
IdempotenceUtil idempotenceUtil;

/**
* 拦截有@IdempotenceRequired 注解 的方法。
*/
@Pointcut("@annotation(xxx.xxx.IdempotenceRequired)")
public void idempotenceMethod(){}

@AfterThrowing(value = "idempotenceMethod()()",throwing = "e")
public void afterThrowing(Throwable e){
if(!(e instanceof IdempotencyException)) {
// 从HTTP header中获取幂等号idempotenceId
String idempotenceId = idempotenceUtil.getHeaderIdempotenceId();
idempotence.record(idempotenceId, 1800);
}
}

@Around(value = "idempotenceMethod()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
// 从HTTP header中获取幂等号idempotenceId
String idempotenceId = idempotenceUtil.getHeaderIdempotenceId();
if(StringUtils.isEmpty(idempotenceId)){
//不存在幂等号则不进行额外操作
return joinPoint.proceed();
}
// 前置操作 幂等号是否存在
boolean existed = idempotence.check(idempotenceId);
if (!existed) {
throw new IdempotencyException("{success:false,message:"操作重复,请重新输入幂等号重试!",data:-2}");
}
//删除幂等号
idempotence.delete(idempotenceId);
Object result = joinPoint.proceed();

return result;
}
}
复制代码
  • 定义个controller
@RequestMapping("/idempotence")
public class IdempotenceController {
    /**
     * 生成幂等号
     * @return
     */
    @GetMapping("/generateId")
    public JsonResult generateId(){
        IdempotenceUtil idempotenceUtil=SpringUtil.getBean(IdempotenceUtil.class);
        String uId=idempotenceUtil.generateId();
        return JsonResult.success("成功生成!").setData(uId);
    }
}
复制代码

好了,实现的代码,就是这些了,理解起来也是比较简单,没有过多复杂的逻辑。

接下来,就是如何使用的问题了,

image.png

这个使用,也是十分的简单啦!!!

幂等的使用

服务端:

不是所有的方法都需要切面拦截 ,只有 IdempotenceRequired 注解的方法才会被拦截。

例如下面接口:

    @IdempotenceRequired
@PostMapping("/getUsers")
public JsonResult getUsers(){

//执行正常业务逻辑
...
}
复制代码

在开发幂等接口时,只需要在方法上简单增加一个 IdempotenceRequired 注解即可。

这基本上就是代码非侵入式了呀!!!

image.png

客户端:

服务端处理好后,在客户端访问接口的时候需要执行以下步骤:

  • 需要先获取幂等号
  • 然后将幂等号添加到请求头中
  • 1.获取幂等号 http://服务地址/idempotence/generateId
null
  • 2.请求调用

往header中添加幂等号

image.png

好了,到这里幂等的实现,就已经完成了!!!^_^

那我们就可以愉快的编写代码了!!!^_^

image.png

END



Java项目构建基础:统一结果,统一异常,统一日志
Spring Cloud 2022.0.0 正式发布,代号 "Kilburn"
13 年,MySQL 之父赌赢了:另起炉灶的 MariaDB 成功上市!
10个常见的 PostgreSQL 错误及避坑指南!

关注后端面试那些事,回复【2022面经】

获取最新大厂Java面经

最后重要提示:高质量的技术交流群,限时免费开放,今年抱团最重要。想进群的,关注SpringForAll社区,回复关键词:加群,拉你进群。

相关阅读

  • 常用保护基——苯甲酸酯类 | Benzoic acid esters!

  • 点击关注,化学科讯!苯甲酸酯是最常用的保护醇的一个酯,苯甲酸酯比乙酸酯难水解。与乙酰基相比,苯甲酰基迁移至邻位羟基上的倾向较小,但它们能够迁移至一个热力学更稳定的位置。
  • Logback 自定义日志脱敏组件

  • 关注我,回复关键字“spring”,免费领取Spring学习资料。前言在我们写代码的时候,会书写许多日志代码,但是有些敏感数据是需要进行安全脱敏处理的。对于日志脱敏的方式有很多,常见
  • 陕西女子凌晨就餐遭多名男子殴打!警方通报来了

  • 近日,陕西咸阳武功县一美食城内,一女子被几名男子追着殴打引发关注。24日,有媒体报道,一女子在武功县一个美食城买饭时,被几个人殴打10多分钟。25日,美食城工作人员向记者确实有此
  • 国内AI工具与信息分享平台,已收集1000+AI工具

  • 欢迎点击上方蓝字⌈维客笔记⌋"关注并星标⭐,不错过每一篇推送!大家好,我是来自1037号森林的BCS!随着ChatGPT的爆火,各种AI工具层出不穷,我想各位小伙伴都急需一个AI工具导航网站,
  • 因为一只狗,丈夫和我离婚了,可我不后悔

  • 玫瑰心语,知音课堂写作训练营3期,复训2期学员。可能有的人已经听过或者见过她的名字了,是的,在过去的两年里,她在知音真实故事公号上一共上稿12篇。扫描下方二维码,免费教你写故事

热门文章

  • “复活”半年后 京东拍拍二手杀入公益事业

  • 京东拍拍二手“复活”半年后,杀入公益事业,试图让企业捐的赠品、家庭闲置品变成实实在在的“爱心”。 把“闲置品”变爱心 6月12日,“益心一益·守护梦想每一步”2018年四

最新文章

  • JACS:北理工最新成果登刊!

  • 点击关注,化学科讯!手性在自然界中普遍存在,并且在化学、物理和生物等许多领域发挥着关键作用。通过外场刺激控制分子手性在基础科学和技术应用方面具有重要的研究意义,因此长期
  • ​Science:超超超燃的1纳米!

  • 点击关注,化学科讯!超薄铁电薄膜用于制备微型和大容量非易失性存储器。对超尺度器件的迫切需求促使人们逐步探索原子尺度铁电薄膜。近几十年来,一些传统的钙钛矿氧化物体系、掺
  • 常用保护基——苯甲酸酯类 | Benzoic acid esters!

  • 点击关注,化学科讯!苯甲酸酯是最常用的保护醇的一个酯,苯甲酸酯比乙酸酯难水解。与乙酰基相比,苯甲酰基迁移至邻位羟基上的倾向较小,但它们能够迁移至一个热力学更稳定的位置。
  • 一种非侵入式幂等性的Java实现

  • 关注我,回复关键字“spring”,免费领取Spring学习资料。作者:llsydn 链接:https://juejin.cn/post/7085944825085689864 今天我们来谈谈什么是幂等性?引用百度百科的解析如下:幂等
  • Logback 自定义日志脱敏组件

  • 关注我,回复关键字“spring”,免费领取Spring学习资料。前言在我们写代码的时候,会书写许多日志代码,但是有些敏感数据是需要进行安全脱敏处理的。对于日志脱敏的方式有很多,常见
  • 又一本AI医学书籍曝光,最大的革新是…

  • 将 脚本之家 设为“星标⭐”第一时间收到文章更新文末包邮送书!!!说到生成型人工智能ChatGPT,恐怕没人不知道吧?毫无意外,它也对医疗界产生了不小震撼。不仅能够书写医疗论文、提