关键词总结:幂等性设计、全局 ID、HTTP 的幂等性
幂等性设计
所谓幂等性设计,就是说,一次和多次请求某一个资源应该具有同样的副作用。(副作用相同而不是结果)
用数学的语言来表达就是:f(x) = f(f(x))。
为什么需要幂等性设计
系统解耦隔离后,服务间的调用可能会有三个状态,一个是成功(Success),一个是失败(Failed),一个是超时(Timeout)。成功与失败是明确的状态,而超时请求方则是完全不知道是什么状态。如果不是幂等的,当请求方再次调用,就会给系统带来不一致的副作用。
例子:
- 订单创建接口,第一次调用超时了,然后调用方重试了一次。是否会多创建一笔订单?
- 订单创建时,我们需要去扣减库存,这时接口发生了超时,调用方重试了一次。是否会多扣一次库存?
两种处理方式
- 上游系统负责明确状态
下游系统提供相应的查询接口。上游系统在 timeout 后去查询一下。如果查到了,就表明已经做了,成功了就不用做了,失败了就走失败流程。 - 下游系统保证状态实现幂等性
通过幂等性的方式。也就是说,把这个查询操作交给下游系统,上游系统只管重试,下游系统保证一次和多次的请求结果是一样的。
第一种方式,需要对方提供一个查询接口来做配合。而第二种方式则需要下游的系统提供支持幂等性的交易接口。
全局 ID
要做到幂等性的交易接口,需要有一个唯一的标识,来标志交易是同一笔交易。而这个交易 ID 由谁来分配是一件比较头疼的事。因为这个标识要能做到全局唯一。
- 中心系统来分配:每次交易需要那个中心系统支持,增加了程序的性能开销
- 上游系统来分配:上游系统可能会是一个集群,同时承担相同的工作,可能会出现 ID 分配重复的问题。
如何解决ID分配冲突的问题
为了解决分配冲突的问题,需要使用一个不会冲突的算法。
可以借助由 Twitter 开源的基于全局唯一 ID 算法实现的分布式 ID 生成算法项目 Snowflake。其核心思想是,产生一个 long 型的 ID
- 前 41 位:毫秒数,需要 69.7 年到达上线。
- 中 10 位:机器编号(前 5 位是数据中心,后 5 位是机器编号),支持 1024 个实例。
- 后 12 位:毫秒内序列号。每毫秒 4096 个序号。
幂等性的处理流程
对于幂等性的处理流程,就是要过滤一下已经收到的交易。当收到交易请求的时候,查询来记录收到的交易的存储,如果查找到了,那么就不再做查询了,并把上次做的结果返回。如果没有查到,那么我们就记录下来。
绝大多数请求应该都不会是重新发送,如果每次请求都去存储里去查一下,这会导致处理流程变得很慢。
这时如果这个存储出现冲突的时候会自己报错就最好了。也就是,收到交易请求 -> 去存储里记录这个 ID(相对于数据的 Insert 操作) -> 如果出现 ID 冲突了的异常,表示之前已经有人发过来了,就不用再做了。
HTTP 的幂等性
GET
- 幂等:是
- 副作用: 无
- 用途:用于获取资源
- GET不会改变资源的状态,不论调用一次还是 N 次都没有副作用。(副作用相同不是结果相同)
HEAD
- 幂等:是
- 副作用: 无
- 用途:用于获取头信息
- 获取接口返回的 Header 数据,每次调用的结果都相同。
OPTIONS
- 幂等:是
- 副作用: 无
- 用途:用于获取当前 URL 所支持的方法
- 获取接口所支持的 HTTP 方法,每次调用的结果都相同。
DELETE
- 幂等:是
- 副作用: 有
- 用途:用于删除资源
- 调用一次和 N 次对系统产生的副作用是相同的,即删掉某个资源
POST
- 幂等:否
- 副作用: 有
- 用途:用于创建资源,对应的 URI 并** 非创建的资源本身**,而是去执行创建动作的操作者
- 两次相同的 POST 请求会在服务器端创建两份资源
PUT
- 幂等:是
- 副作用: 有
- 用途:用于创建或更新操作,所对应的 URI 是要创建或更新的资源本身
- 对同一 URI 进行多次 PUT 的副作用和一次 PUT 是相同的
POST的幂等性设计
对于 POST 的方式,很可能会出现多次提交的问题,比如我们在论坛中发贴时,有时候因为网络有问题,可能会对同一篇贴子出现多次提交的情况。
- 首先,在表单中需要隐藏一个 token(表单的唯一标识)。(这种情况其实是通过前端生成 ID 把 POST 变成了 PUT。)
- 然后,当用户点击提交后,后端把数据和这个 token 保存在数据库中。如果有重复提交,那么数据库中的 token 会做排它限制,从而做到幂等性。
- 更为稳妥的做法是,后端成功后向前端返回 302 跳转,把用户的前端页跳转到 GET 请求,把刚刚 POST 的数据给展示出来,如果是 Web 上的最好还把之前的表单设置成过期。PRG 模式(Post/Redirect/Get)
参考资料:
左耳听风(极客时间)链接:
http://gk.link/a/10f5D
GitHub链接:
https://github.com/lichangke/LeetCode
CSDN首页:
https://me.csdn.net/leacock1991
欢迎大家来一起交流学习