原文地址:http://www.ws-rest.org/2014/sites/default/files/wsrest2014_submission_7.pdf
约定
T:Transaction 事务
S:Service 服务
R:Request 请求
A:Assumption 假定
C:Corollary 推论
A1:事务T会发送多个请求R到多个RESTful服务S。
例:事务T需要预定两个航线的航班,R1预定航线S1,R2预定航线S2。
A2:任意请求R都有可能在一个临时状态失败。
例:R2可能因为航线S2无票而失败。
如果没有其他可能失败的原因,只需要每个请求R能保持幂等性即可。
如果请求R因为临时原因失败,幂等性可以通过重试来保证请求R成功调用;但是有些请求失败不能通过重试来完成,比如没有足够的票。
A3:整个事务T必须是原子性的。
所有属于T的请求R要么全部成功,要么全部失败。
A4:服务S会为事务T的请求R临时预留资源。
例:航线S1会为事务T预留一个座位,但是不会永远预留:如果T成功,则成功预订此座位;如果T失败,S1会释放这个座位,以供其他用户预订。
C1:事务T中的每个请求R都有可能需要在执行后被取消执行。
每个请求R都需要有一个对应的取消方法。
从A1+A2可以得出:如果有中间处理出错(业务逻辑出错、服务挂掉、网络抖动等),有可能出现不完整事务(事务的一部分请求成功,一部分请求失败)。
例:R1成功,R2失败。
从A3可以得出:需要取消执行R1,以恢复整个事务T的原子性。
C2:每个服务S需要对每个请求R提供一个确认方法(confirm),并且执行每个请求R的时候,需要等待对应的confirm方法调用。
从A4可以得出:服务S会为请求R预留资源。
从C1可以得出:每个请求R都可能被取消执行。每个服务S在执行请求R之后,会等待事务T发送确认信号,这样R才不会被取消。只有T才知道所有请求都成功执行了,所以需要T来调用确认方法。
例:航线R2也被预留完毕之后,T会确认R1、R2,然后进入付费流程。
协议
1.客户端依照一个工作流与多个服务端交互。
2.每次交互都有可能引起服务状态变更。
每个服务都有对应的URI,这个URI可能被用作Confirm请求,或者被用作Cancel请求。如果服务等待Confirm/Cancel请求超时,服务会默认自己执行Cancel。
3a.如果一个事务中的所有请求按照流程执行成功之后,事务调用Confirm URI来Confirm每个请求。
3b.如果一个事务中的任一请求执行失败了,事务调用Cancel URI来Cancel已经执行的请求。
这里的Cancel URI是已经被执行的请求对应的,不包含那些尚未被执行的请求。
原子性保证
如果事务在3a/3b之前中止了,每个参与的服务会在超时之后自动Cancel。
如果事务达到了3a/3b,每个参与的服务就能收到Confirm/Cancel请求了。
如果3a/3b执行的太晚,以至于参与的服务超时然后自动单方面的Cancel了,此时事务一致性会被破坏。这种情况下,需要人工介入干预。
协议设计细节
Confirm - Commit
Cancel - Rollback
Cancel
每个参与者服务,在等待一段时间超时之后,要自行取消已经执行的操作。
Confirm
必须尽快提交每部分操作,防止某个参与者服务因为超时而自动回滚。
Timeout
Transaction Coordinator
为了重用,把事务协调器独立出来,避免自己处理各种失败。
这个协调器可以作为一个服务存活,分布式服务调用方、各个服务参与者通过某种方式与协调器通信。
分布式事务调用方可以提交批量需要Confirm的请求到协调器,然后协调器充作代理发送Confirm请求到各个服务参与者。
如果调用Confirm超时:
1.所有参与者都超时了、或者都被Cancel了。
这种能保证一致性。
2.部分参与者Confirm了,部分参与者超时了、或者连不上了。
这样无法保证一致性,事务协调器需要解决这个问题。
Recovery
1.参与者服务的幂等Confirm
如果事务协调器自己挂了,重启之后,需要重新调用Confirm;
如果参与者服务挂了、或者网络原因连不上,事务协调器只需要重试Confirm即可。
2.事务协调器的幂等Confirm
调用者服务需要自己记住哪些Confirm请求需要重试。
优化
如果执行过程中有业务级别的异常,不能让参与者一直占有着资源直到超时。
1.参与者取消
需要Cancel的时候,尽早同志参与者服务。
2.事务协调器取消