跳至主要內容

分布式设计中的幂等性处理

Hirsuntech大约 14 分钟

分布式设计中的幂等性处理

分布式系统设计中,接口幂等性的处理是必须要考虑的重要问题。

很多开发人员对幂等性了解不多,在实际代码开发中也很少关注。

其实幂等性的概念非常简单,它的含义是:一次接口调用与多次相同的接口调用,能够得到与预期相符的结果。

基本架构设计案例

以京东金融为例,京东金融有大量的应用系统,比如审计系统和前端应用。它们需要与后台的数据仓库进行交互,通常选择 RESTful 或 RPC 的方式进行调用。这里以 RESTful 为例进行讲解。

1735284799165.png
1735284799165.png

系统之间通过网络传输,网络可能会出现断网的情况。为了保证系统的高可靠性,前端应用系统可能会增加如 Spring Retryable 这样的组件,通过不断重试发送相同调用消息的方式来提高系统的可靠性。

这种方式会带来一个附加问题:后台服务必须考虑幂等性。

案例分析

1735284874073.png

假设有一个RESTful接口用于将编号为1的员工的工资上调500元。很多新手程序员会这样写代码:

1735285080877.png
  1. 从数据库中通过 select by ID 得到当前员工的基本工资信息。
  2. EMP.setSalary 在原有工资基础上加上参数中的500。
  3. 执行更新操作。

单看这三行代码没有问题,但忽略了接口的幂等性处理。

如果前端应用为了保证高可用,通过底层组件发送了多次重复请求,每执行一次,员工的工资就会增加500,执行十次则增加5000。预期是在原始工资基础上加500,但实际可能加了5000,这就破坏了幂等性。

解决方案

传统解决方案

在业务代码上进行前置判断,通过数据库中的标识判断员工是否已经调过薪,或者记录上次调薪的时间来进行判断。这种方法有两个问题:

  1. 需要做前置幂等判断的地方可能太多,容易漏掉。
  2. 增加程序员的工作量和复杂度,特别是对实习工程师来说,难以保证他们能考虑到幂等性的问题。

无侵入的幂等解决方案

构建一个与业务无关的通用幂等解决方案,称为幂等表。幂等表的设计如下:

  1. 增加两个组件:应用网关 和 Redis。
  2. 应用网关职责:对应用发来的请求进行过滤和转发。
  3. Redis职责:存储最近执行的请求编号。

构建幂等表 in redis 是我们的通用解决方案。

处理过程

1735286170879.png
1735286170879.png
  1. 请求编号生成:前端应用系统在每次发送 RESTful 或 RPC 调用时,在请求头或RPC头部附加一个唯一的 request ID
  2. 网关检查:请求到达网关,网关通过 Nginx 和 Lua 脚本检查 Redis 中的幂等表。如果 request ID 不存在,保存到幂等表中,状态设置为 proc(处理中)。
  3. 请求处理:业务逻辑处理完成后,更新Redis中的 request ID 状态为 OK(处理成功)。
1735286457321.png
1735286457321.png

重复请求处理:如果相同的 request ID 再次发送,网关直接返回错误编码 201,前端应用接收到 201 编码后排除本次请求。

考虑 redis 突然断网怎么办

潜在的影响

  • redis 断网
  • 新的重试被拒绝,应用系统显示处理失败
  • 数据服务的数据变更执行完成
  • 无法把 ok 写到 redis

不妨先写 ok 入redis,再执行数据变更业务。

存活时间的设置

Redis中的幂等表数据设置存活时间(如5分钟),作用有两个:

  1. 防止内存占用过多:及时清除过期数据,释放内存。
  2. 处理异常情况:数据服务在处理中途崩溃,导致请求一直处于处理中状态,存活时间到期后数据被清除,允许再次处理请求。

代码实现

通过AOP(面向切面编程)实现无侵入的幂等处理:

  1. 注解定义:在控制器方法上增加自定义注解。
  2. AOP拦截:AOP在方法执行后,通过后置通知更新Redis中的 request ID 状态。
1735287438788.png

优缺点分析

优点

  1. 无代码侵入:无需修改业务逻辑,只需增加注解即可。
  2. 通用性强:适用于所有系统,保证接口幂等性。

缺点

  1. 前台改造:要求在请求头中附带唯一的请求编号,并处理自定义错误编码201。
  2. 架构复杂度增加:增加了Nginx和Redis组件,可能增加运营和开发成本。

总结

幂等表的设计通过Redis和应用网关,确保每个业务请求只被处理一次,避免重复请求带来的问题。

虽然增加了一些架构复杂度,但提供了一个通用的解决方案,适用于多个系统。