TL;DR
订单最后都要关联到上层支付系统(如微信/支付宝/Apple Pay)之上,使用 HTTP API 进行操作时会存在未知状态,可以通过实现各个操作之间的幂等接口,结合回调与状态查询,实现各层系统之间状态最终一致。
订单最后都要关联到上层支付系统(如微信/支付宝/Apple Pay)之上,使用 HTTP API 进行操作时会存在未知状态,可以通过实现各个操作之间的幂等接口,结合回调与状态查询,实现各层系统之间状态最终一致。
通过唯一编号确定同一请求,没有唯一编号的自行生成。
数据库记录操作状态,数据库事务保证数据一致性。
通过HTTP API进行通信的系统,在支付或者只允许操作一次的相关场景中,对接口的幂等性有严格要求。
接口的幂等性体现在:
请求执行成功所得到的结果与次数无关
如果接口没有实现幂等性,对于转账的应用场景:
在这一场景下,整个流程正常,接口无论是否实现幂等性与否都对执行结果没有影响。
在这一场景,重试成功的情况下,接口无论是否实现幂等性与否都对执行结果没有影响。
在实际应用场景中,接口超时的情况并不罕见,接口超时不代表操作失败,可能存在的情况就有操作实际成功然而并没有返回数据。在这样一个场景之下,接口没有实现幂等性造成重复操作,对于系统的可靠性来说是不可容忍的。
两次操作同时发出,并且都成功,接口没有实现幂等的情况下,两次转账操作都会成功,但是对于用户A来说,实际上这是同一次的转账意愿。
以上的场景还是在A与B账户均存在于同一个资源(一般为数据库)之上的操作,如果A与B账户处于两个资源,场景还会更加的复杂。
由上述的场景可以看出,实现接口幂等性的两个方向在于:
利用数据库实现上述两个需求十分方便。
定义同一次操作
使用数据库实现发号器,为每一次请求生成唯一编号
拒绝重复操作
通过数据库事务以及唯一索引,以请求编号作为依据,保证同一时间内只有一个请求进行操作,经过先查询后操作的方式,已完成操作不执行更改逻辑,保证请求值执行一次。
以MySQL为例,针对需要实现幂等的操作,可以建立如下的数据表:
1 2 3 4 5 6 7 8 |
CREATE TABLE `idempotent_op` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `op_no` char(32) NOT NULL DEFAULT '', `status` int(11) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `op_no` (`op_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; |
其中op_no
列存在唯一索引。
针对上述转账的场景,设定A与B都处于同一数据库中,可以用如下伪代码表示上述的转账操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# 非事务读,判断是否当前请求已处理,减少事务数量 IF SELECT op_no_A IN idempotent_op AND (status OF op_no_A) == 'FINISHED' THEN RETURN 'TRANSFER HANDLED' BEGIN TRANSACTION # 在事务读中使用X锁,保证只有一个请求完成本次转账 IF SELECT op_no_A IN idempotent_op FOR UPDATE THEN IF (status OF op_no_A) == 'FINISHED' THEN ROLLBACK RETURN 'TRANSFER HANDLED' ELSE IF DO_TRANSFER(A, B, money) == SUCCESS THEN SET status OF op_no_A FROM 'CREATED' TO 'FINISHED' COMMIT ELSE ROLLBACK RETURN 'TRANSFER FAILED' ELSE # 数据库唯一键保证同时到达的多个新请求只有一个可以进行转账操作 result = INSERT (op_no=op_no_A, status='CREATE') INTO idempotent_op IF result = FAIL THEN ROLLBACK RETURN 'TRANSFER INSERT RECORD FAILED' IF DO_TRANSFER(A, B, money) == SUCCESS THEN SET status OF op_no_A FROM 'CREATED' TO 'FINISHED' COMMIT ELSE ROLLBACK RETURN 'TRANSFER FAILED' |
以上。
第1章 微服务
微服务就是一些协同工作的小而自治的服务。
大多数系统强调高内聚,低耦合,即通过抽象或者模块保证代码的内聚性。
一个基本的特征就是微服务是一个独立的实体。无论是在容器还是在进程中存在。
微服务通过API进行解耦合。
微服务的优点集中在:
第2章 演化式架构师
架构师的一个重要职责是,确保团队有着共同的技术愿景,以帮助我们向客户交付他们想要的系统。
架构师要改变那种从一开始就要设计出完美产品的想法,而选择设计出一个合理的框架,在这个框架下可以慢慢的演化出正确的系统,一旦学到了跟过的的还是,可以加以使用。
架构师的职责之一是保证该系统适合开发人员在其之上工作。
架构师专注大方向,在优先的情况下参与到非常细节的具体实现上。
架构师关注服务边界之间的问题,而不应当过多关注边界内部的问题。
一个好服务至少要做到如下三个方面的优势:
+ 监控
+ 接口
+ 架构安全性
COBIT(Control Objectives for Information and Related Technology)给出的的治理的定义是:
治理通过评估干系人的需求、当前情况及下一步的可能性来确保企业目标的达成,
通过排优先级和做决策来设定方向。对于已经达成一致的方向和目标进行监督。
第3章 如何建模服务
服务要高内聚,低耦合,划分微服务的一个方式可以是找到各自功能的限界上下文(Bounded Context)。
第4章 集成
集成的几个原则:
共享数据库会带来的问题:
+ 所有使用者需要了解schema之间的细节
+ 消费方被绑定了技术
基于请求/响应模式的同步请求易于实现,而基于事件的异步请求模式则能应对长时操作以及降低耦合度。
编排(Orchestration)与协同(Choreography)的区别在于是否有有中心。编排会通过中心驱动流程。编排优点是状态和流程明确,问题在于中心负担过重,导致其他协作方过于单薄;协同的优点在于解耦合,问题在于需要额外的监控流程。异步是便于实现协同模式的通讯方式。
对于基于事件的异步协作方式,需要关注的地方在于事件的发布机制和接收机制。发布机制中需要注意的有:
+ 消息中间件要尽量简单,不要混杂业务逻辑
服务即状态机。
权衡DRY与微服务过程耦合的冲突,原则上是微服务内部DRY,跨服务可以适当违反DRY。
服务客户端的开发的要点是:
+ DRY
+ 处理与服务本身职责没有关系,但是又影响服务大规模运行部署的一些基础功能的部分(服务发现,故障模式,日志),只包含处理底层传输协议的代码
+ 由客户端决定升级时机
RPC与REST相比,客户端和服务端的部署无法分离。
第5章 分解单块系统
“接缝”的定义是,系统中可以抽取相对独立的一部分代码,这部分代码进行修改不会影响系统其他部分,是划分服务边界的依据。
分块的一些实例:
+ 外键约束通过业务逻辑实现
+ 对于共享的数据,通过单一服务单元进行操作
+ 共享的数据库表,拆分字段单元
+ 报表的导出,要么通过异步逻辑(提交请求异步导出),要么用一些软件(如列数据库),或者独立程序定期生成报表数据到其他数据库(类似InfluxDB中INTO的表现)
第6章 部署
每个微服务都建议有自己的CI流程。
如果可能,应该将每个服务都放到单独的主机或者容器之中,
部署的关键在于各个步骤的自动化。
第7章 测试
测试时主要关注对场景的测试,而非面面俱到。
想要频繁的发布版本,需要尽可能频繁的发布小范围的改变。
蓝/绿部署和金丝雀发布的区别在于,蓝绿是切全部流量,金丝雀发布是引导部分流量。并且Canary版本验证的内容会包括功能与非功能的,两个版本共存时间更长。Canary版本的好处在于可以对效果做更多的干预,通过实际运行效果评估开发的效果。
微服务中MTTR表现良好胜于MTBF。
第8章 监控
Web提供给监控系统的指标数据,最低要求就是提供响应时间和错误率。
为了便于监控系统追踪请求,可以使用全局ID的方式,贯穿整个请求的流程。
对于系统来说,对于数据的聚合,可以:
+ 聚合CPU一类的的主机层级的指标及应用程序程标(帮助找到程序性能瓶颈)
+ 要能回溯存储数据(Nagios的瓶颈,存储时间太短,可以加入第三方的存储组件)
第9章 安全
交给单点登录网关的应该是粗粒度的身份认证,而系统级别/业务级别的认证控制,应该在微服务内部实现。
权衡服务间的信任问题,可以根据操作的敏感程度,从低到高选择边界内隐式信任,验证调用方身份(验证access_token?),要求调用方提供原始主体凭证(如支付宝支付密码?)。
不要自己实现加密解密!不要发明自己的安全协议!要用行业的通用方式。
Datensparsamkeit:只存储完成业务运营或者满足当地法律所需的信息。没有存储,就没有丢失。
第10章 康威定律和系统设计
康威定律:任何组织在设计一套系统(广义概念上的系统)时,所交付的设计方案在结构上都与该组织的沟通结构保持一致。
团队结构影响开发成果(反康威定律也是有可能的)。
第11章 规模化微服务
设计微服务时的态度,就是假设一切都会失败。
权衡实现的复杂度,可以通过可接受的损失程度确定。
考虑跨功能服务发生故障时的场景,能明确定位和错误处理的实现。
级联的架构,必须要有保护机制,防止全站不可用。
主动制造故障驱动系统强壮性的增强(因人而异,无需极端追求)。
尽力追求操作幂等。
作业与执行逻辑分离,即通过worker实际执行任务,虽然可能中断或者执行缓慢,但是不至于造成任务丢失。
CAP中只能存在AP系统和CP系统。AP系统要实现最终一致,而CP系统通过拒绝服务保证C。CA系统因为牺牲了分区容忍性,根本不能跨网络运行,在分布式系统中,这完全与分布式不符合。
服务发现是规模化的一个重要组成部分。常规方案有:
+ DNS,通过名字引导流量到服务,缺点在于灵活性以及时效性不足
+ 动态服务注册,通过ZK等软件
第12章 总结
一切去中心化是微服务一个重要原则。
不了解系统承载的业务和特点之前,不要微服务化!不能自动化,不要微服务化!
> 变化是无法避免的,所以,拥抱它吧!
作者推荐书单:
其他关键词