关于接口设计:接口幂等该如何设计和实现
前言在程序开发的过程中是否遇到如下的问题: 同一件商品手速很快多点击了几次,在后盾生成了两笔订单。同一笔订单点了因为网络卡顿,点了两次领取,后果发现反复领取了。微服务架构下利用间通过RPC调用失败,进入重试机制,导致一个申请提交屡次。黑客利用充值抓包到的数据,进行屡次调用充值、评论、拜访,造成数据的异样。这些问题均能够通过接口幂等性设计来解决。幂等性意味着同一个申请无论被反复执行多少次,都能产生雷同的后果,不会导致反复的操作或不统一的数据状态。 在古代分布式系统中,接口的幂等性设计和实现至关重要。本文将深入探讨接口幂等的重要性、实现办法以及可能面临的挑战,并提供测试接口幂等性的无效策略。 什么是接口幂等性接口幂等性指的是一个接口或操作在雷同的申请参数下,无论被执行多少次,其后果都是统一的且不会产生副作用。换句话说,如果一个申请曾经胜利执行,再次执行雷同的申请应该不会对系统状态产生任何额定的影响。例如,一个获取用户信息的接口就是幂等的,因为屡次获取同一个用户的信息不会扭转零碎的状态。 相同,非幂等接口可能会导致反复的操作和潜在的问题。以领取操作为例,如果没有实现幂等性,反复领取可能会给用户和商家带来不必要的麻烦和损失。 为什么须要接口幂等性避免反复操作:幂等性能够确保零碎不会因为反复的申请而产生反复的操作,从而防止数据谬误和不统一。进步系统可靠性:在网络不稳固或其余异常情况下,反复的申请是很常见的。幂等性能够帮忙零碎解决这些反复申请,而不会导致系统出错或不稳固。加强用户体验:用户不须要放心因为不小心反复操作而导致的问题,从而进步了用户的应用体验和满意度。简化错误处理:因为幂等接口能够平安地解决反复申请,因而在处理错误和复原时更加容易,缩小了简单的谬误复原逻辑。如何设计接口幂等性应用惟一标识:为每个申请调配一个惟一的标识,例如申请 ID 或流水号。通过在申请中传递这个惟一标识,零碎能够判断是否曾经解决过该申请。设计幂等的操作:确保操作自身是幂等的。例如,更新数据时能够采纳"更新或插入"的策略,而不是间接批改已有记录。应用事务:在波及多个数据库操作的状况下,应用事务来确保整个操作的原子性和幂等性。利用缓存:将申请的后果缓存起来,当接管到雷同的申请时,间接返回缓存中的后果,防止反复执行操作。如何实现接口幂等性以下实现形式是基于demo实现,用于阐明幂等性的设计和实现。 惟一标识:能够通过生成全局惟一的 ID(如 UUID)来标识每个申请。在申请的参数中蕴含这个 ID,服务器在解决申请时能够依据 ID 来判断是否曾经解决过该申请。服务端生成 requestId 之后将 requestId 放到redis中,当然须要给 ID 设置一个生效工夫,超时的 ID 也会被删除。 public class RequestIdGenerator { public static String generateRequestId() { Stirng uuid = UUID.randomUUID().toString(); putCacheIfAbsent(uuid); return uuid; }}在接口中,将生成的申请 ID 与申请参数一起传递给服务器。 // 生成申请 IDString requestId = RequestIdGenerator.generateRequestId();// 构建申请参数Map<String, String> requestParams = new HashMap<>();requestParams.put("requestId", requestId);requestParams.put("otherParam", "value");// 发送申请httpClient.sendRequest(requestParams);服务器在接管到申请后,能够依据申请 requestId 来判断是否曾经解决过该申请,并进行相应的解决。 当后端接管到订单提交的申请的时候,会先判断requestId在缓存中是否存在,第一次申请的时候,requestId肯定存在,也会失常返回后果,然而第二次携带同一个requestId的时候被回绝了。 幂等的操作:以订单状态更新为例,如果订单曾经处于最终状态(如已领取或已发货),再次更新订单状态不会扭转其理论状态,因而是幂等的。public class OrderService { public void updateOrderStatus(String orderId, OrderStatus status) { // 依据 orderId 获取订单 Order order = orderIdToOrderMapper orderIdToOrder(orderId); // 判断订单是否处于最终状态 if (order.isFinalStatus()) { // 订单已处于最终状态,不须要进行理论的更新操作 return; } // 更新订单状态 order.setStatus(status); orderRepository.save(order); }}事务:在数据库操作中,能够应用事务来保障操作的原子性和幂等性。如果某个操作失败,事务能够回滚到之前的状态,防止不统一的数据。@Transactionalpublic void performTransactionalOperation() { // 开启事务 Transaction transaction = transactionManager.beginTransaction(); transaction.setIsolationLevel(IsolationLevel.READ_COMMITTED); transaction.setPropagationBehavior(Propagation.REQUIRED); // 数据库操作 1 //... // 数据库操作 2 //... // 提交事务 transactionManager.commit();}开启事务是一种乐观锁实现的形式,一开始更新数据就把数据加锁了,具备强烈的独占和排他个性。 ...