1。为什么需要幂等性?在分布式场景下,在多个业务系统之间实现一个强一致的协议是极其困难的。最简单且可实现的假设之一是确保最终一致性,这要求服务器在处理重复请求时给出相同的响应,同时不会对持久化数据造成副作用(即多次操作和单次操作的结果需要是从业务角度来看是一致的)。如果API是幂等的,调用的发起者可以安全地重试。这符合我们的一般假设。提供幂等能力是服务提供者需要做的,所以这篇文章是从服务提供者的角度来写的,也就是下面的“我们”通常指的是服务提供者。2、如何进行幂等判断那么我们如何对一个请求进行幂等判断呢?首先,我们需要一种方法来区分什么是“一次性”请求。其次,我们需要在API实现逻辑中针对不同的数据持久化方式进行不同的判断。2.1请求唯一标识通常我们的API需要添加bizId,它可以标识请求的唯一性,调用发起者用它来标识业务考虑的唯一业务需求。必须注意的是,它是“业务唯一标识符”,而不是技术唯一标识符,两者有本质区别。事实一:但是,在大多数情况下,调用的发起者可能并不知道如何为请求生成一个有效的唯一标识,这就需要我们在业务对接的过程中进行更多的沟通。2.2需要多长时间才能做到幂等做到幂等就是说我们一定是持久化了一些数据,但是任何数据都不可能永久存在,都需要一个有效期。对于大多数请求,建议的幂等有效期为三个月。一些特殊的场景甚至可以是一年。但几乎不再。有效期是一个明确的契约,这意味着我们可以定期对持久化数据做一些数据治理工作。同时,超过有效期的请求基本都会幂等地失败,所以它的后果就是“同样的事情,过几个月又做了一次”。2.3写入类型如果我们的业务逻辑是要在持久化存储中写入一些东西,那么最好的方式就是添加一个唯一的key,通常由user_id、operationtype、biz_id组成。操作类型是我们系统设计定义的业务操作类型,加上它是为了避免不同操作类型之间的相互干扰。加user_id的原因是我们平时分库分表。对于可以写入和更新数据的场景,插入需要放在最前面,否则幂等性可能不起作用。比如下面的库存操作场景,如果把两条SQL写反了,在库存售罄的场景下就不会幂等了:insertinventorydeductionflow;updateinventorysetavailableinventory=availableinventory-1其中库存id=?库存可用性>0;当唯一键冲突时,我们需要捕获它,这很可能是幂等的。为什么是高概率而不是确定性?这是因为前面提到的“事实1”。调用者可能会错误地重用请求唯一编号,重试时可能会改变一些核心参数。因此,当uniquekey发生冲突时,我们需要做如下操作:根据uniquekey查询之前写入的数据。检查关键信息。比如我们提供的API是发放优惠券,那么我们至少需要验证这些信息:接收用户ID、优惠券模板、优惠券面额、有效期等。当关键信息不一致时,会出现类似于“DUPLICATE_BUT_DIFFERENT_REQUEST”被返回;验证通过后,返回发送成功的优惠券Id。这个非常重要。不验证关键信息就返回幂等成功是大多数场景下失败的根本原因。2.4更新类型更新类型最大的挑战是我们没有地方存放一条数据的多个更新行为,所以大多数情况下,我们需要使用状态机来推测某个更新行为是否已经发生,以及更复杂的情况可能需要我们添加一个专用的幂等表。2.4.1使用状态机业务中的大部分数据处理都是有状态的,比如交易订单。此时首选的方法是通过状态机的时序来判断某个更新行为是否已经完成。但这并不容易,我们需要非常仔细地看清业务的各种规则和限制,同时,除了更新状态,大部分情况下都会伴随更新一些其他的附加信息,我们还需要检查附加信息是否已经按照请求的要求进行了更新。2.4.2添加幂等表通过添加幂等表,将更新类型转换为2.3节描述的写入类型。但是需要注意的是,幂等表需要和当前更新的表在同一个事务中,否则会失效。幂等表需要增加一个字段表示数据写入/更新时间,方便我们定时清理过期数据。2.5删除建议对存储使用逻辑删除而不是物理删除,这样我们就有办法判断删除操作是否已经完成。删除往往意味着数据处于最终状态,所以最好从幂等性的角度来处理;如果业务设计删除的数据不是最终状态,那么你需要非常小心,因为这违反了一般的设计原则。3.总结幂等性可以说是分布式应用的基石。如您所见,实现它并不像大多数人最初想象的那么简单。做好它需要我们从业务语义的角度去设计和思考。文/苏沐关注德武科技,成为最时尚的科技人
