接口幂等性及如何实现幂等性

接口幂等性是确保多次调用同一接口不会改变结果的关键。常见的应用场景包括防止订单重复提交、表单重复发送。实现幂等性可通过前端限制按钮点击、后端基于唯一业务单号或token机制。查询操作天然幂等,删除与新增需考虑业务单号,更新操作可能需要乐观锁。token机制在无唯一标识时提供保护,确保操作唯一性。

接口幂等性及如何实现幂等性

概述

幂等性,用数学函数表示为,f(f(x)) = f(x),即幂等元素运行多次,还等于原来运行的结果。延伸到我们java接口上就是,一个接口调用多次(每次入参相同),与这个接口调用一次的结果相同。生产中与接口幂等性相关的业务问题有如下:

1)提交订单按钮如何防止重复提交?

2)表单录入页如何防止重复提交?

3)微服务接口,客户端重试时,会对业务数据产生影响吗?

带着以上问题,来看看如何实现接口的幂等性。

一、我们什么情况下会多次调用接口的场景

1、网络延迟,导致调用接口后为能及时响应,此时会多次点击重试;

2、抖动造成连续点击触发接口的按钮;

3、人为恶意连续多次调用接口。

二、如何防止接口多次调用

前端页面实现

1、前端页面实现:对于前端触发的接口调用,由于网络延迟或失误多次点击按钮导致的,可在前端对按钮进行设置,接口返回调用结果前,禁止再次点击按钮,但前端的实现方式无法根本上规避此类问题;

2、人为恶意调用时,一般会绕过前端调用接口,此时只能从后端服务器进行处理。

后端如何实现接口幂等性

这里讨论的幂等性通常指的是对数据库crud来说的,一般我们接口幂等性设计时也是要结合实际来看,比如下如下场景我们就需要做接口的幂等性设计

1)多次点击提交订单,后台应该只生成一个订单;

2)支付时,由于网络问题重复,应该只扣一次钱;

并不是所有的接口都需要实现接口幂等性,而我们实现接口幂等性也有一个核心思想,就是通过唯一的业务单号保证幂等。

后端一般为4类接口进行,查询、删除、插入及更新;

查询

查询不会对数据存储造成影响,查询操作无论执行多少次,最终结果都是一致,所以查询时天然幂等性的,一般我们无需对查询操作进行特殊处理;

删除

删除操作一般分为有唯一业务单号和无唯一业务单号两种,

1)对于有唯一业务单号,比如数据id,由于第一次删除操作时,已经将数据进行删除,第二次删除时,由于无法找到唯一业务单号对应的数据,因此此时删除操作对数据时没有影响的,所以我们可以认为此时是幂等的;

2)对于没有唯一业务单号的删除操作,如删除没有审核的数据,由于第一次删除后,在下一次删除操作前,可能出现生成新的未审核的数据,造成多次删除操作均删除了数据,所以为非幂等,我们需要进行特殊处理设计接口的幂等性;一般使用token机制进行解决。

新增

新增操作同样也分为有唯一业务单号和无唯一业务单号两种;

1)有唯一业务单号,第一次新增成功,第二次新增时,由于唯一业务单号已经生成了数据,再次新增数据无法成功,所以此类情况为幂等;

2)无唯一业务单号,同样需求认为添加唯一业务单号处理,一般也是用token机制解决;

更新

更新操作分为3种

1)有唯一业务单号,同时每次更新会记录更新次数,那这种情况下,更新操作就不是幂等的,我们可以用乐观锁的方式实现,给数据添加版本号;

2)有唯一业务单号,不记录更新操作,此时就是幂等的;

3)无唯一业务单号,同删除操作,此时是非幂等的,需要用到token机制进行解决。

三、token机制

跳转到接口操作页面时,后台生成一个唯一的序列号,调用接口时传入改序列号,接口校验序列号确实存在,用该序列化生成分布式锁;

Java 项目中实现接口幂等性,主要是为了确保同一个请求被多次执行时,其结果与执行一次相同,不会对系统造成副作用。常见的实现方式包括: ### 常见实现方式: 1. **Token 机制(唯一请求标识)** - 客户端每次请求时生成一个唯一 Token,并在请求头或参数中携带。 - 服务端接收到请求后,先检查 Token 是否已存在(如 Redis 缓存),存在则拒绝重复处理。 - 请求处理完成后,将 Token 存入缓存,设置与业务逻辑匹配的过期时间。 2. **数据库唯一索引** - 对于涉及数据插入或变更的接口,可以在数据库中设置唯一索引(如订单号、业务流水号等)。 - 如果重复提交导致唯一索引冲突,则抛出异常并提示客户端请求已处理。 3. **乐观锁** - 在更新数据时使用版本号(version)或时间戳(timestamp)。 - 每次更新前检查版本号是否一致,不一致说明数据已被修改,拒绝重复请求。 4. **Redis 分布式锁** - 使用 Redis 缓存记录请求标识,设置与业务逻辑匹配的过期时间。 - 在执行业务逻辑前,尝试获取锁,获取失败说明请求正在处理或已完成。 5. **业务状态判断** - 对于特定业务流程(如支付、订单状态变更),在执行操作前检查当前业务状态是否允许操作。 - 例如:订单已支付,则拒绝重复支付请求。 ### 示例代码(基于 Token 的幂等性校验): ```java @RestController public class IdempotentController { @Autowired private RedisTemplate<String, String> redisTemplate; @PostMapping("/submit") public ResponseEntity<String> submitRequest(@RequestHeader("token") String token) { String key = "request_token:" + token; Boolean exists = redisTemplate.hasKey(key); if (exists != null && exists) { return ResponseEntity.status(HttpStatus.DUPLICATE).body("请求已处理,请勿重复提交"); } try { // 执行业务逻辑 // ... // 设置 Token 到 Redis,过期时间根据业务需求设定(例如 5 分钟) redisTemplate.opsForValue().set(key, "processed", 5, TimeUnit.MINUTES); return ResponseEntity.ok("请求处理成功"); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("处理失败"); } } } ``` ### 注意事项: - 需要根据业务场景选择合适的幂等性实现方式。 - 对于高并发场景,需结合分布式锁和缓存机制,确保性能和一致性。 - 幂等性通常需要客户端与服务端协同配合,避免重复请求问题。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值