生成环境中Web接口幂等性保证实践记录

关于幂等性

在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。 这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。

  • Http规范中对幂等性的定义

A request method is considered “idempotent” if the intended effect on the server of multiple identical requests with that method is the same as the effect for a single such request. Of the request methods defined by this specification, PUT, DELETE, and safe request methods are idempotent.

如果对服务器的多个相同请求的预期效果与单个此类请求的效果相同,则请求方法被视为“幂等”。规范定义的请求方法中,PUT,DELETE和安全请求方法应该是具有幂等性的。

分布式系统中接口幂等性问题

  • 一个订单流程可能会遇到的问题
    • 订单创建接口,第一次调用响应超时了,然后调用方进行了重试。
    • 用户对订单进行了支付,服务端发生了扣钱操作,但是接口响应超时了,然后调用方进行了重试。
    • 用户对订单进行了充值,第三方服务回调服务器,但因为第三方服务不稳定,并发调用了多次。
    • 订单状态更新接口,调用方连续发送了两个消息,一个是已创建,一个是已付款。但是你先接收到已付款,然后又接收到了已创建。
    • 订单完成之后,需要发送一条短信,当一台机器接收到短信发送的消息之后,处理较慢。消息中间件又把消息投递给另外一台机器处理。
  • 解决之道
    • 保证接口幂等性,接口可重复调用,在调用方多次调用的情况下,接口最终得到的结果是一致的。
    • 需要保证幂等性的接口:增加、更新、删除类型。
    • 天然幂等的接口:查询一次和多次,对于系统来说,没有任何影响,查出的结果也是一样。

分布式系统中接口幂等性保证

  • 全局唯一ID

根据业务的操作和内容生成一个全局ID,在执行操作前先根据这个全局唯一ID是否存在,来判断这个操作是否已经执行。如果不存在则把全局ID,存储到存储系统中,比如数据库、redis等。如果存在则表示该方法已经执行,保证幂等性。

  • 使用去重表约束操作

适用于在业务中有唯一标识的场景。在支付的流程中,因为一个订单只会支付一次,订单ID为唯一标识。新建一张去重表,用于记录已支付订单,并把订单ID作为唯一索引,把支付成功与写入去重表放在一个事务中,利用数据库的UNIQUE KEY 进行约束保证幂等性。

  • 多版本控制

适合于需要多次更新的场景,如商品信息,可以在更新的接口中增加一个版本号,每次请求携带新的版本号,避免重复提交带来的问题,保证幂等性。

update commodity set name='newName',version='version' where id='id' and version<'version'
  • 状态机控制

适合在有状态机流转的情况下,如订单的状态迁移,通过表字段paystatus表示订单的状态,如UNPAID(待支付) SUCCESS(成功) FAIL(失败),利用订单的状态迁移与单条UPDATE语句的原子性保证幂等性,通过SQL执行的成功与失败来进行下一步操作。

update recharge_order set  paystatus = SUCCESS where orderId= 'orderid' and paystatus = UNPAID

image