外网请求如何保证幂等

常见的外网请求,通常来自网页或app。一个不幂等的接口,可能会导致用户只点击一次,却产生多次点击的效果。

  1. 如果用户的请求只是修改昵称,那么基本没影响
  2. 但如果用户的请求是扣费,比如送礼,那就会产生多扣费的资金问题。

问题的原因

在互联网上,无论是内网还是外网,网络经常会有不稳定的情况。为应对这些网络问题,通常会有各种重试策略。

  1. 内网不稳定的情况,通常会在代理上(如nginx)配置一些超时重试,或50x重试策略
  2. 外网不稳定的情况,客户端会在超时时进行重试或切域名重试等

这些重试策略,导致了用户端只发出一次请求,实际服务端收到多次请求的情况。

总结:“重试”是产生问题的源头

解决问题的关键

解决问题的关键其实很简单,就是要让服务端能识别接收到的“多个”请求是否是“同个”请求,从而确保业务只执行一次。

如何标识请求的唯一性

本质上是请求的参数中,包含由一个或多个参数组合而成的,唯一且不变的标识。

  1. 像修改用户昵称的请求,业务本身就幂等,因为用户id是唯一且不变的
  2. 像用户的扣费请求,那么通常的做法是使用唯一的订单号,以及相应的唯一ID生成策略

唯一ID算法需要考虑哪些

  1. 有序
    • 唯一ID作为数据的索引,保持有序有助于数据库性能提升
  2. 数据库类型
    • 以MySQL为例,选择bigint(20)还是varchar,前者只有64位,像”2017072809364399365840049582“这种订单号只能用varchar存储
  3. 基因
  4. 性能

前端唯一ID生成方案

  1. 使用UUID (缺点:无序)
  2. 通过后端接口获取唯一ID(缺点:多一次外网的网络请求)
  3. 按一定的业务规则生成,如:timestamp+ 用户ID(10-11位)+ 随机3位数字(或递增)(优点:有序且基本保证唯一)

后端唯一ID生成方案

  1. 使用snowflake算法实现的唯一ID服务
  2. 其他业界方案

解决方案

  1. 前端唯一ID使用方案3
    • 后端服务对前端生成的ID进行规则校验,防止恶意伪造不规范的唯一ID
  2. 后端唯一ID
  3. 直接使用前端传入的唯一ID
  4. 从唯一ID服务获取(和前端唯一ID进行映射)

扩展

不同业务唯一ID冲突问题

  • 若后端作为一个基础服务,对接上层业务,每个业务使用的唯一ID规则不一样。那么如何避免业务之间唯一ID冲突?
    1. 使用业务ID+业务唯一ID进行订单唯一性区分
    2. 统一唯一ID的生成规则(统一从基础服务获取或由该服务提供订单ID申请接口)

跨机房的服务幂等问题

  • 若服务部署多个机房(通常每个机房有对应的数据库),如何保证幂等,因为外网请求重试,不一定会到达同一个机房。
    1. 把用户按机房分区
    2. 不考虑跨机房幂等问题(主备架构下同个请求落在不同机房概率不高,收益低)