REST无事务?HTTP当然不会有标准的事务原语,但自己实现一个也好啊。
dlee教路,《RESTful Web Services》第8章的Resource Design一节里有教。
作者把事务也作为一种资源:
- 首先访问获得事务资源
client:POST /transactions/account-transfer HTTP/1.1
server:Location: /transactions/account-transfer/D29271F549AFF9889647883591248B53
- 然后在此资源下进行操作,比如存款55块
PUT /transactions/account-transfer/D29271F549AFF9889647883591248B53/accounts/saving/55
- 用PUT原语访问事务资源提交事务,或者用DELETE原语删除事务资源回退事务
PUT /transactions/account-transfer/D29271F549AFF9889647883591248B53 HTTP/1.1
如果换回RPC的句式,就是先transaction.begin()获得事务ID,然后在每个操作中使用事务ID作为参数之一,最后以事务ID为参数提交或回退,不过RESTers喜欢这样ROA的文法。
gomba
是一个RESTful的Web->DataBase框架,很好的实践了上文的做法。创建了一个事务后,框架会将事务ID与一个数据库Connection绑定在一起,放在一个全局可访问的地方,比如ServletContext或者Spring的ApplicationContext。每次操作都根据ID把connection拿出来,并更新connection的访问时间,最后事务提交就把connection提交。另外还有一个定期扫描的线程,将所有过期的Connection关闭,放回连接池。
可以看到,这只是一种聊胜于无的事务方案。首先,如果客户端既要更新本地数据,又要访问远程服务,这时会存在两个事务分开的事务,而无法像WS-TX那样联成一个全局事务。这样,无论先提交谁,后面一个出错时前一个已提交的事务都无法回滚。
然后,很明显gomba
这种服务端实现,非常的浪费connection资源。
但是,传统SOAP的WS-TX暂时也不是好东西,.Net 需要.Net 3.0,Java里也只有Glassfish+Metro支持的相对能用。
最后,让我们面对Web无标准事务协议的事实吧,Salesforce就做得不错
- 提供批量操作接口,将远程操作尽量一次过在本地事务的最后执行。
- 让客户自行补偿操作吧,并没有想象的这么困难。
Add操作时返回ID,就可以执行Delete的反向操作。Update前先clone原始数据,就可以重新执行Update原始数据,Delete时服务端并不真正删除,就可以提供UnDelete的接口。
像Update里,返回原始数据之前,如果已经被另一个人Update了呢?如果更新操作会引起服务端的连锁操作,不能靠简单的逆向操作支持,就如同火箭发出去了不能回退呢?
前一种情况实际影响不大,后一种情况就只有自求多福了,但愿自己的项目中没有这么倒霉的场景了。