微服务中的幂等设计
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
放入leveldb
、redis
、memcache
,下次请求时判断该值是否存在,存在直接返回错误否则放行。
如果不是使用nginx
或者实施上述方案有困难,可以在网关层实现,对于java
语言(其他语言也类似),可以做到透明化处理:在网关里注册Idempotentfilter
,该filter
提取UUID
,同样通过leveldb
、redis
、memcache
等高速缓存判断是否可以放行。
显然token
方案可以尽早发现和拦截订单,大大降低资源消耗,减少数据库压力,对业务也是透明无浸入,但只依赖token
机制显然是有缺陷
的:
当缓存服务出错ldb
文件丢失,redis
数据意外清空,memcache
意外重启等,会导致我们的UUID标志丢失,这时就需要数据库来兜底,数据库的唯一约束是不可或缺的,当出现这些极端情况时,即使请求达到DB
,也不会造成数据重复。
有幂等需求的接口,建议采用token
机制实现高效的排除重复请求,当然最终落地需要结合具体的业务场景。
以上是关于微服务中的幂等设计的主要内容,如果未能解决你的问题,请参考以下文章