微服务中的幂等设计

Posted 北京IT内推圈儿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了微服务中的幂等设计相关的知识,希望对你有一定的参考价值。

当前浏览器不支持播放音乐或语音,请在微信或其他浏览器中播放

幂等简介

来自曾任职于华为,聚美,目前是准阿里的一位小哥给大家分享微服务中的幂等设计。

幂等概念原本是数学运算里的概念,维基百科对其的定义为:

  • 在某二元运算下,幂等元素是指被自己重复运算(或对于函数是为复合)的结果等于它自己的元素。

  • 某一元运算为幂等的时,其作用在任一元素两次后会和其作用一次的结果相同。例如,高斯符号便是幂等的。

衍生到微服务领域,幂等指的是使用相同的参数多次调用相同的API,对后端产生的影响是一致的。

许多人理解为多次调用返回的结果是一样,这种观点是错误的,连最基本的查询接口也不可能是多次查询返回同样的结果,幂等的侧重点是对后端的影响,并不关心返回的数据

幂等是所有客户端-服务端系统都需要考虑的问题,无论你是C-S还是B-S模式,但在B-S模式下,这个问题更为突出,尤其是近几年流行的微服务架构下,这是因为在微服务模式下,单个应用简单,但服务整体更为复杂,完成一个功能需要走较长的调用链,整体链路可能由不同的语言不同框架实现,每个微服务为了保证服务的质量,常常会对一些不确定的因素(如:网络异常资源耗尽等)导致的问题进行容错处理,典型措施就是重试

上图是互联网应用中非常精简的调用流程,应该说许多公司的链路远比这个复杂,流程中的每个环节都可能出现重试:

  • 用户可能会频繁点击按钮

  • 浏览器在网络不稳定情况下重发请求

  • 反向代码由于网关响应超时或者网络异常重发到另外一个实例

  • 网关同样可能因为超时/出错自动调用另外一个service

如果请求只涉及到资源查询,显然调用多次并不会对后端数据发生更改,所以查询类的API天然就是幂等的但如果这是一个创单接口,我们就不得不考虑幂等的问题,创单过程中会涉及到库存扣减余额扣减订单数据入库消息通知等等修改后端资源的操作。

为了防止这类请求重复提交到后端,前端页面通常会在用户点击提交后禁用按钮,后端成功后清空表单跳转到提示页面:

实现幂等的四种方式

  • 数据库唯一约束:通过业务设计,确定几个可以唯一识别订单字段,并设置为唯一索引,这种方式的好处就是简单,代码基本不做改动,缺点也很明显,所有的重复请求要穿透整个调用链路,一直到数据库才能判重,对链路上资源消耗很大,会给数据库带来巨大的压力,即使服务扩容,TPS也很难上去。

  • MVCC(多版本并发控制):操作时带上版本号:update t1 set x=y,version=version+1 where version=xxx,优点是提升了并发响应能力,实现也简单,缺点是只适用更新接口,还是会将重复请求达到数据库,数据库压力较大。

  • 状态机机制,本质上是 MVCC 方式的变种:订单有多个业务状态,每次操作数据会带上一个状态,只有在上一个状态匹配的情况下会更新数据,优缺点和 MVCC 大同小异,但这种机制解决了插入的问题,不仅仅适用在更新接口。

  • Token 机制:这是一种非常高效的幂等机制,在性能和功能上都达到很好的效果,接下来我们就详细讨论下这种机制的实现。

MVCC状态机机制,应该就是CAS这种乐观锁的实现,通过减少锁的争用来提升服务端并发能力,本句是读者自己理解不是作者本人写的

幂等-Token机制实现

Token机制的核心就是要求客户端的每次请求里必须携带一个UUID,产生UUID的算法很多,如:雪花算法ObjectID,常用的开发语言也有对应的实现,即使client生成UUID有困难,也可以调用ID生成器预先生成一批缓存到本地,这里就不一一展开。

有了这个UUID,可以在多个环节实现拦截,而且这种判断是非常高效的,几乎都是O(1)的时间复杂度,远比数据库唯一约束判断快。

譬如在nginx里,我们可以使用lua获取到请求里的UUID,将该UUID放入leveldbredismemcache,下次请求时判断该值是否存在,存在直接返回错误否则放行。

如果不是使用nginx或者实施上述方案有困难,可以在网关层实现,对于java语言(其他语言也类似),可以做到透明化处理:在网关里注册Idempotentfilter,该filter提取UUID,同样通过leveldbredismemcache等高速缓存判断是否可以放行。

显然token方案可以尽早发现和拦截订单,大大降低资源消耗,减少数据库压力,对业务也是透明无浸入,但只依赖token机制显然是有缺陷的:

当缓存服务出错ldb文件丢失,redis数据意外清空,memcache意外重启等,会导致我们的UUID标志丢失,这时就需要数据库来兜底,数据库的唯一约束是不可或缺的,当出现这些极端情况时,即使请求达到DB,也不会造成数据重复。

有幂等需求的接口,建议采用token机制实现高效的排除重复请求,当然最终落地需要结合具体的业务场景。

以上是关于微服务中的幂等设计的主要内容,如果未能解决你的问题,请参考以下文章

接口服务中的幂等性设计和防重保证,详细分析幂等性设计几种实现方法

分布式服务的幂等性设计

支付接口的幂等性设计

浅谈常用接口中的幂等设计

如何避免重复提交?分布式服务的幂等性设计!

什么是分布式系统中的幂等性