智慧印刷工坊

智慧印刷工坊

Spring Boot + Redis 实现接口幂等性 | 分布式开发必知

admin 43 76

一、概念

幂等性,通俗的说就是一个接口,多次发起同一个请求,必须保证操作只能执行一次比如:

订单接口,不能多次创建订单

支付接口,重复支付同一笔订单只能扣一次钱

支付宝回调接口,可能会多次回调,必须处理重复回调

二、常见解决方案

唯一索引--防止新增脏数据

token机制--防止页面重复提交

悲观锁--获取数据的时候加锁(锁表或锁行)

乐观锁--基于版本号version实现,在更新数据那一刻校验数据

分布式锁--redis(jedis、redisson)或zookeeper实现

状态机--状态变更,更新数据时判断状态

三、本文实现

本文采用第2种方式实现,即通过redis+token机制实现接口幂等性校验

四、实现思路

为需要保证幂等性的每一次请求创建一个唯一标识token,先获取token,并将此token存入redis,请求接口时,将此token放到header或者作为请求参数请求接口,后端接口判断redis中是否存在此token:

如果存在,正常处理业务逻辑,并从redis中删除此token,那么,如果是重复请求,由于token已被删除,则不能通过校验,返回请勿重复操作提示

如果不存在,说明参数不合法或者是重复请求,返回提示即可

五、项目简介

springboot

redis

@ApiIdempotent注解+拦截器对请求进行拦截

@ControllerAdvice全局异常处理

压测工具:jmeter

说明:

本文重点介绍幂等性核心实现,关于springboot如何集成redis、ServerResponse、ResponseCode等细枝末节不在本文讨论范围之内,有兴趣的小伙伴可以查看作者的Github项目:

六、代码实现

pom


JedisUtil





自定义注解@ApiIdempotent


1.ApiIdempotentInterceptor拦截器


TokenServiceImpl



TestApplication


OK,目前为止,校验代码准备就绪,接下来测试验证

七、测试验证

获取token的控制器TokenController


TestController,注意@ApiIdempotent注解,在需要幂等性校验的方法上声明此注解即可,不需要校验的无影响


获取token

查看redis

测试接口安全性:利用jmeter测试工具模拟50个并发请求,将上一步获取到的token作为参数

header或参数均不传token,或者token值为空,或者token值乱填,均无法通过校验,如token值为"abcd"

八、注意点(非常重要)

上图中,不能单纯的直接删除token而不校验是否删除成功,会出现并发安全性问题,因为,有可能多个线程同时走到第46行,此时token还未被删除,所以继续往下执行,如果不校验(token)的删除结果而直接放行,那么还是会出现重复提交问题,即使实际上只有一次真正的删除操作,下面重现一下

稍微修改一下代码:

再次请求

再看看控制台

虽然只有一个真正删除掉token,但由于没有对删除结果进行校验,所以还是有并发问题,因此,必须校验

九、总结

其实思路很简单,就是每次请求保证唯一性,从而保证幂等性,通过拦截器+注解,就不用每次请求都写重复代码,其实也可以利用springaop实现。