接口幂等性是指在网络请求中,对同一个API接口进行多次相同的请求,与只进行一次请求所产生的效果是一样的。这意味着无论客户端请求一次还是多次,服务器端的处理结果和系统的状态变更都应当是确定的,保持一致。
重复操作情况
网络问题
- 请求超时:客户端发送请求后,由于网络延迟或服务器处理时间长,客户端没有收到响应,可能会重新发送请求。
- 网络波动:在请求发送过程中,网络连接不稳定,导致请求重复发送。
- TCP重传:TCP协议在数据包丢失时会自动重传,可能导致服务器收到重复的请求。
应用层问题
- 负载均衡器问题:在使用负载均衡器的情况下,如果请求处理时间较长,可能会在负载均衡器上产生多个相同请求的副本。
用户行为
- 用户重复点击:用户在操作界面时可能会不小心多次点击按钮,导致重复提交表单或请求。
- 浏览器刷新:用户在操作完成后刷新页面,可能会重新发送之前的请求。
分布式系统
- 消息队列:消息队列中的消息可能会因为消费者处理失败而被重新投递,导致消费者重复处理消息。
故障恢复
- 系统恢复:在系统故障恢复后,可能会重新执行之前未完成的操作。
- 数据恢复:数据恢复过程中可能会触发之前已经执行的操作。
接口幂等方案
数据库唯一主键
通过为数据库中的表设置唯一约束或唯一索引,可以确保同一份数据只能插入一次。
例如,在创建订单的场景中,可以使用订单编号作为唯一主键,如果尝试插入一个已经存在的订单编号,数据库会拒绝执行这个操作,从而保证了接口的幂等性。
防重 Token 令牌
在某些情况下,可以给每个请求分配一个唯一的Token,并且在业务逻辑中检查该Token是否已经被处理过。如果没有,则处理请求并标记该Token为已处理;如果有,则直接返回之前处理的结果。这种方法适用于需要确保请求幂等性的场景,比如支付接口。
基于Redis实现的防重Token令牌的流程主要分为以下几个步骤:
- 客户端发起请求获取Token。
- 服务端接收到请求后,生成一个唯一的Token,并将其存储到Redis中。
- 服务端将生成的Token返回给客户端。
- 客户端在后续的业务操作接口请求中,将此Token放在HTTP Header中一起发送给服务端。
- 服务端在接收到业务操作接口请求后,先从Redis中检查此Token是否存在。
- 如果Token存在,说明这是第一次请求,服务端删除Redis中的Token并继续执行后面的业务逻辑;如果Token不存在,说明这是一个重复请求,服务端不再执行后续业务逻辑。
- 最终,服务端将业务操作的结果返回给客户端。
下游传递唯一标识符
在微服务架构中,不同的服务之间经常需要互相调用来完成一项业务流程。为了保证接口的幂等性,可以生成一个全局唯一的请求标识符(比如一个UUID),并将它作为请求的一部分传递给下游服务。下游服务接收到请求后,首先检查这个唯一标识符是否已经存在于其本地缓存或数据库中。如果已经存在,那么表示这个请求之前已经被处理过。这种方法与防重Token令牌方法类似,主要区别在于Token是由上游服务在请求时传递给下游服务的。
状态机
状态机(State Machine)是一种抽象模型,它定义了一组有限的状态集合,以及从一个状态到另一个状态的转换规则。状态机可以是确定性的(每个状态对应一个明确的输出)或非确定性的(从一个状态出发可能有多个不同的输出)。
状态机可以帮助我们管理复杂的业务流程,并确保在任何情况下,多次执行同一操作都能得到相同的结果。具体来说,我们可以为每个业务对象(如订单、交易等)定义一个状态机,其中包括以下几个方面:
定义状态:
- 根据业务需求定义一系列状态,例如订单的状态可以包括:“未支付”、“已支付”、“已完成”、“已取消”等。
- 每个状态都应该清晰地描述了业务对象当前所处的情况。
定义状态转换规则:
- 确定哪些外部事件(如用户支付、系统取消订单等)可以触发状态转换。
- 设计状态之间的转换规则,规定只有当满足某些条件时,才能从一个状态转移到另一个状态。
- 状态转换应该是单向的,一旦某个状态被改变,就无法回退到前一个状态,除非有特定的回滚机制。
实施状态机:
- 在服务端实现状态机逻辑,每次接收到外部请求时,先检查当前业务对象的状态。
- 如果当前状态允许转换,则执行相应的业务逻辑,并更新状态;否则,返回一个指示当前状态不允许此操作的响应。
RPG模式
PRG(POST/Redirect/GET)模式是一种前端交互策略,旨在解决用户刷新页面时可能导致表单数据重复提交的问题。
- POST:
- 用户提交一个表单时,首先发送一个 HTTP POST 请求到服务器。这个请求包含了表单的所有数据。
- 服务器接收并处理这个 POST 请求,执行相应的业务逻辑(如创建新记录、更新现有记录等)。
- Redirect:
- 处理完 POST 请求后,服务器不会直接返回处理结果给客户端,而是发送一个 HTTP 302 重定向响应。这个响应包含了一个新的 URL,通常是刚刚创建或更新的对象的详情页。
- 客户端浏览器接收到重定向响应后,自动发起一个 GET 请求访问指定的新 URL。
- GET:
- 服务器处理 GET 请求,并返回相应的 HTML 页面或 JSON 数据,显示处理结果。
使用 PRG
模式的主要目的是为了防止重复提交。如果用户在提交表单后刷新页面,浏览器只会按照常规方式重新发起GET请求,而非重新提交POST数据,因此有效地避免了重复提交引发的潜在问题。
参考链接: