java接口的幂等性及解决方案

一、什么情况下需要幂等

用户多次点击按钮
用户页面回退再次提交
微服务相互调用,由于网络问题,导致请求失败,feign触发重试机制

二、幂等性解决方案

2.1 token机制(令牌)

在加载页面详情时候,服务器会顺便生成一个token一起返回给前端,服务端同时也在Redis中保存这个token数据,前端并不展示这个token,但当页面点击提交按钮时候,会在携带上这个token参数,此时后端便会先校验前端提交请求的token与redis中的token是否一致,一致的话即是第一次请求,执行业务代码并在Redis中删除该token,当用户还是携带上次的验证码多次提交,此时服务器判断redis中验证码不存在,便可能是多次提交的情况,不再执行业务代码,保证业务的幂等性,不被重复执行。
问题1:先删除token还是后删除token
·先删除可能导致, 删除token以后,服务器闪断,业务确实没有执行,此时重试还带上之前token,由于防重设计导致,请求还是没有走到执行业务代码那块逻辑。
·后删除可能导致,业务处理成功,但是服务闪断,出现超时,没有删除token,别人继续重试,导致业务被执行两遍
解决:我们最好设计为先删除token,如果业务调用失败,就重新获取token再次请求。
问题2:如何保证token 获取、比较和删除必须是原子性
redis.get(token) 、token.equals、 redis.del(token)如果这3个操作不是原子,可能导致,高并发下,都get到同样的数据,判断都成功,导致业务并发执行
解决:可以在redis使用lua脚本完成这个操作

//  redis+lua脚本 原子验证令牌防止重复提交攻击
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        String orderToken = "现在的令牌";
        //  return 0 失败  1 成功
        Long result = stringRedisTemplate
### 接口幂等性的设计方案与最佳实践 #### 幂等性的重要性 在软件开发中,接口幂等性是指无论同一请求被发送多少次,其最终的结果都是一致的。这不仅能够提升系统的可靠性,还能减少因网络波动或其他原因引发的重复操作问题[^1]。 #### 唯一标识符的设计 为了确保每条请求具有唯一性,通常会引入一个全局唯一的 ID 来标记该请求。此 ID 可以基于 UUID 或其他自定义算法生成,在每次发起请求时作为参数传递给服务器端验证。如果发现相同 ID 的请求已经存在,则可以直接返回之前的响应结果而无需再次执行逻辑处理[^2]。 ```java public class IdempotentService { private static final Set<String> processedRequests = new HashSet<>(); public Response handleRequest(Request request) throws DuplicateRequestException { String uniqueId = request.getUniqueId(); synchronized (processedRequests) { if (!processedRequests.contains(uniqueId)) { // 执行业务逻辑并保存成功状态至集合中 processBusinessLogic(request); processedRequests.add(uniqueId); return buildSuccessResponse(); } else { throw new DuplicateRequestException("Duplicate Request Detected"); } } } private void processBusinessLogic(Request request){ // 实际业务逻辑处理代码... } private Response buildSuccessResponse(){ // 构建成功的响应对象... return null; } } ``` 上述示例展示了如何利用 `Set` 数据结构来存储已处理过的请求 ID ,从而防止重复请求被执行两次以上的情况发生 。需要注意的是,在高并发场景下可能还需要考虑线程安全以及内存占用等问题 。 #### 数据库乐观锁的应用 当涉及到更新资源类的操作时(如修改账户余额),可以通过版本号字段配合 WHERE 子句条件来进行控制。只有当前记录的实际版本号等于预期值时才允许更新操作完成;否则拒绝此次更改尝试,并提示客户端重新获取最新数据后再做相应调整。 ```sql UPDATE accounts SET balance=balance-?, version=?+1 WHERE id=? AND version=? ``` 在此 SQL 片段里,“version” 字段用于追踪实体的状态变更次数。“?” 占位符分别代表扣除金额、现有版本加一后的目标数值、账户主键以及原始版本号。这样即使有多个事务几乎同时针对同一个账户发起转账指令也不会造成资金丢失或者错误累积的现象出现 。 #### 分布式锁机制 对于那些无法单纯依赖本地缓存或单一数据库实例解决冲突的服务组件来说,采用 Redis 等支持分布式的工具构建外部锁定服务不失为一种可行的选择之一。它可以帮助协调不同节点之间的竞争关系,使得即便是在复杂的微服务体系环境下也能维持良好的一致性表现水平^。 ```java @RedisLock(key="order_create_lock", timeoutSecs=5L) public Order createOrder(CreateOrderDTO dto)throws Exception{ // 创建订单的具体流程 ... } // 自定义注解实现细节省略 .. ``` 这里展示了一个简单的例子说明怎样借助 Spring AOP Redisson 库快速集成分布式互斥功能到现有的方法签名当中去。开发者只需关注核心业务部分即可自动获得相应的保护屏障效果 。 --- ### 总结 综上所述,要实现接口幂等性可以从以下几个方面入手:一是通过设置全局唯一标志符区分不同的请求批次;二是运用数据库层面提供的乐观锁特性保障敏感属性不受干扰破坏;三是必要时候引入第三方中间件产品辅助达成跨域范围内的同步协作目的。当然具体实施方案还需结合项目实际情况灵活选用最适配的技术路线才行[^3]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

百里东君~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值