(4)弹力设计篇之“幂等性设计”(id生成原则?long比uui
系统解耦隔离后,服务间的调用可能会有三个状态,一个是成功(Success),一个是失败(Failed),一个是超时(Timeout)。前两者都是明确的状态,而超时则是完全不知道是什么状态。
例子:
订单创建接口,第一次调用超时了,然后调用方重试了一次。是否会多创建一笔订单?
当这笔订单开始支付,在支付请求发出之后,在服务端发生了扣钱操作,接口响应超时了,调用方重试了一次。是否会多扣一次钱?
两种处理方式。
(1)查询上游系统接口。查到了,就表明已经做了,失败了就走失败流程。
(2)幂等性的方式。也就是说,把这个查询操作交给下游系统,多次结果一样。
全局 ID
要做到幂等性的交易接口,需要有一个唯一的标识,来标志交易是同一笔交易。而这个交易 ID 由谁来分配是一件比较头疼的事。因为这个标识要能做到全局唯一。
如果由一个中心系统来分配,那么每一次交易都需要找那个中心系统来。 这样增加了程序的性能开销。如果由上游系统来分配,则可能会导致可能会出现分配 ID 重复了的问题。因为上游系统可能会是一个集群,它们同时承担相同的工作。
需要使用一个不会冲突的算法,比如 UUID ,但它的字符串占用的空间比较大,索引的效率低,生成的 ID 太过于随机,没有递增,不能按前后顺序排序。
在全局唯一 ID 的算法中,这里介绍一个 Twitter 的开源项目 Snowflake。它是一个分布式 ID 的生成算法。其核心思想是,产生一个 long 型的 ID,其中:
41bits 作为毫秒数。大概可以用 69.7 年。
10bits 作为机器编号(5bits 是数据中心,5bits 的机器 ID),支持 1024 个实例。
12bits 作为毫秒内的序列号。一毫秒可以生成 4096 个序号。
处理流程
对于幂等性的处理流程来说,要过滤一下已经收到的交易。需要一个存储来记录收到的交易。
当收到交易请求的时候,到存储中去查询。查找到了,把上次结果返回。没查到,记下来。
但是,上面这个流程有个问题。绝大多数请求应该都不会是重新发过来的,所有请求都到去查一下,会很慢。直接去存储里记录 Insert ,用ID 冲突异常来判断。
小结
超时是我们需要解决的问题。实现全局 ID(id生成算法)。Twitter 的 Snowflake 是全局 ID 实现。幂等性接口的处理流程。