你好,我是华仔。
上一节我们讲解了微服务架构“数据分布式”挑战的应对方式:分布式事务。分布式事务都是基于接口或者消息来协作的,而分布式环境下,接口调用可能会失败,消息可能丢失,我们只能采取重试的方式来尽力保证数据最终能够达到一致性。重试机制就涉及微服务架构设计的另外一个技巧:全局幂等。
其中,幂等(Idempotence)是一个数学与计算机科学中的概念,它指的是某个操作或函数可以多次执行并且保持同样的效果,即无论执行多少次,结果都不会改变。具体来说,如果一个操作是幂等的,那么对同一个输入重复应用这个操作将不会产生额外的效果或变化。
举个最简单的例子:当我们在网上购物,使用微信或者支付宝支付的时候,点击确认支付后网络正好断了,你也不知道是否支付成功,于是又重新刷新支付了一次。对于微信和支付宝来说,如果不做幂等判断,就会出现一个订单支付了两次的情况;而有了幂等判断,就能够保证一个订单只支付一次。
你可能会有疑问,为什么我们要在幂等前面加上“全局”两个字呢?其实这也是微服务架构的“服务分布式”特点决定的:同样的接口请求或者消息,并不一定会被同一个微服务实例处理。因此我们需要在全局的多个微服务范围内保证“幂等”,所以叫“全局幂等”。
全局幂等方案总体设计原理是将业务操作理解为一个对象,对象本身具备“全局唯一标识”和“状态”,通过状态控制来实现幂等特性。不同的全局幂等模式,区别主要体现在“全局唯一标识”如何生成、如何判断幂等状态、应用在什么场景这三个方面。
接下来,我们就来详细讲解全局幂等常见的模式和技巧。
事务级同步处理模式指的是在分布式事务处理中,将消息处理和业务处理同步进行的模式。其基本原理如下:

假设A服务是分布式事务的发起者,B服务是分布式事务的执行者,A服务和B服务都基于数据库实现全局幂等。具体的处理步骤如下:
上述处理步骤中,如果A服务在指定时间内没有收到B服务返回的执行结果,可能的原因有4种:
A服务发出的消息丢失了(对应步骤2)
B服务返回的消息丢失了(对应步骤4)
也可能是B服务挂掉了
B服务事务处理过程中出异常了
无论是哪种情况,A服务都需要重发事件消息,如果多次重发(一般尝试3次)后还是失败,A服务可能要采取告警等手段来提示人工进行处理。
事务级同步处理模式比较适合需要同步处理、处理简单、处理速度快的场景,一般通过接口调用来交互,例如使用TCC分布式事务Try接口扣库存。
事务级异步处理模式指的是在分布式事务处理中,将消息处理和业务处理分开进行的模式。其基本原理如下:

可以看到,事务级异步处理模式整体逻辑和“事务级同步处理模式”大体一致,区别在于B服务的处理有所不同。B服务收到事件消息后,先保存事件消息(对应上图步骤3),再异步处理业务操作,完成后再更新本地的事件消息状态。
事务级异步处理模式比较适合需要处理速度慢、处理时间长的场景,一般通过消息队列消息来交互,例如使用SAGA分布式事务来处理项目管理过程中的一些流程。
前面我们讲解的两种模式主要应用在分布式事务协作的场景。除此以外,一些关键的业务操作和接口调用也需要保证幂等,例如用户下单、支付等。这就需要我们实现接口级自动幂等的处理。
接口级自动幂等指的是提供接口的微服务在接口处理逻辑里面实现幂等,防止异常情况下,业务重复处理导致用户损失或者用户体验不好。
接口级自动幂等的基本原理如下:

假设A服务是接口调用方,B服务是接口提供方,其处理逻辑如下:
接口级自动幂等处理过程中使用的全局ID,可以由调用方生成,例如在前端生成唯一Token,也可以从接口参数中组装出来,例如“用户ID + 订单ID”来标识一次支付请求。
全局幂等方案中的关键技术点就是如何进行“幂等判断”,其核心思想是通过“状态”来进行幂等判断。简单的状态可以用“是”和“否”来代表是否已经处理过了,复杂的状态就需要用“状态机”来进行状态判断了,例如订单状态包括“Pending Payment”“Paid”“Shipped”等。
具体实现的时候,我们可以基于数据库、Redis、ZooKeeper等系统。下面简单介绍一些常用的实现幂等判断的技术手段。
首先是数据库唯一索引。数据库唯一索引可以保证不会插入重复数据,而全局幂等中的“全局唯一标识”就可以用来做唯一索引,通过判断插入数据是否成功就可以实现简单的“是”和“否”的幂等判断。这种方式一般用于创建数据的业务场景,例如用户注册、创建订单等。
其次是Redis SETNX命令。Redis SETNX命令用于在指定的key不存在时,设置key的值。我们可以通过创建名字是“全局唯一标识”的key来实现简单的“是”和“否”的幂等判断。这种方式一般用于分布式事务、定时任务调度等场景。
然后是ZooKeeper创建节点。ZooKeeper创建节点的时候,如果节点已经存在则会失败。我们可以创建名为“全局唯一标识”的节点来实现简单的“是”和“否”的幂等判断。这种方式和Redis SETNX的方式基本类似,但性能上要差一些,且要维护ZooKeeper集群,整体要复杂和麻烦一些。如果系统已经用了Redis,更推荐基于Redis来做。
最后是状态机判断。如果全局幂等的状态比较复杂,则需要使用“状态机”来进行幂等判断。一般使用数据库来保存状态,在更新的时候使用“Update …… Where …… and status = XXX”的方式来进行幂等处理。
这一节课,我们重点讲解全局幂等的常用处理模式和技巧。其中“事务级同步处理模式”适合短时分布式事务的处理,“事务级异步处理模式”适合长时分布式事务的梳理,“接口级自动幂等”适合业务上需要进行防重复处理的场景。常用的简单状态的幂等判断可以基于数据库唯一索引、Redis、ZooKeeper等系统来实现,复杂状态的幂等判断需要结合状态机来实现。
以上就是今天的全部内容,最后留一道思考题给你吧:你现在做的业务中是否有全局幂等的处理?如果有的话,采用的是什么处理模式和技术手段?
欢迎你把答案写到留言区,和我一起讨论。相信经过深度思考的回答,也会让你对知识的理解更加深刻。