REST (或者至少是 HTTP)的一个重要方面就是一些操作(动作)是幂等的。正如 Gregor Roth 在多年前所说的那样:
PUT 方法是幂等的。幂等的方法意味着请求成功执行所得到的的结果不依赖于该方法被执行的次数。
幂等也是在 SOA 设计模式中所讨论的一个问题。多年以来,围绕着 REST 以及相对于其他方式它所能带来的收益已经有了很多的讨论,在这个过程中,幂等只是一个方面,通常我们认为已经对其有所理解了,实际上并没有进行充分地研究。但是,最近在面向服务架构(Service Oriented Architecture)邮件列表中有一个帖子,针对这个看起来很直观的理念,激发了很多的讨论。初始的帖子是这样的:
目前我正在审视我们服务的幂等性并提出一个应用于企业的规定性指导,这个指导将会用于创建幂等服务的场景之中。问题在于,所有的服务都需要是幂等的吗?这现实吗?
它引用了另外的一篇 blog ,在这篇文章中,作者这样说:
当然,对于服务的提供者来说,实现幂等会比较复杂。而复杂性就会花费金钱。这可能就是幂等的服务如此稀少的原因。[…] 但是,服务只要成为幂等的,那么它就会非常简单,并且能够保证跨集成边界的数据一致性。服务 _ 应该 _ 是幂等的。这应该是架构和服务设计的原则。尤其是对于那些被很多消费者使用的服务更应如此。你不能将保持数据一致的复杂性推给你的消费者,对吧?消费者就是你的客户,你应该这样善待他们。如果你能为他们带来便利的话,你会有更多的消费者。
初始的这个帖子引起了很多的响应,包括来自 Cap Gemini 的 Steve Jones ,他这样说到:
幂等是 IT 行业中巨大的垃圾词汇之一,它的意思是“内存中的幂等”(idempotent in memory)。如果你有一个评论的服务,它具备更新的功能,从服务调用者的视角来看,服务必须要维护和管理状态。那你的含义就是它能够在内存中横向扩展。如果你更新状态的话,包括在数据库中,那么就不能保证是幂等的。幂等指的是当你使用相同的值调用相同的功能时,结果完全一致,这是它的数学定义。如果你改变过状态,那么就不是幂等的,而“数据库更新”并不会改变任何事情。
Mark Baker 曾经在 REST 这个话题上曾经有过很多回复,回应了 Steve:
就像你的评论样例所述,确实存在非幂等的状态更新,将一些数据更新为特定的值也会是幂等的更新…因为很显然如果用相同的输入执行多次的话,结果会是相同的。
Steve 在这一点上与 Mark 的观点是一致的:如果具有相同输入的一个操作没有改变任何的事情,那么它是幂等的。但是,如果这个操作改变了任意的状态,比如说它记录了这个请求的最后时间,那么它就不是幂等的。
在 IT 行业中,这个术语长久以来被人们滥用了,这一点我“感触颇深”,我曾经多次见过这样的场景,人们宣称一些事情是“幂等的”只是因为它在内存中是无状态的,尽管其消费者要达到的效果是记录性的事务。
Mark 同意 Steve 的严格定义,但是他相信这不能应用于初始请求的上下文之中,也就是:分布式系统中的幂等性:
[…] 这个词汇只对于接口有意义而不是实现。所以,如果我定义一个操作,比如说“set”,并将其定义为幂等的,那么它会只关系到客户端,即便在这个接口的实现中会产生日志。
另外一个参与者 Ashaf Galal,断言所有“读的服务”(reading services)都是幂等的,因为这样的服务只会返回数据。但是,Steve 指出,这是一个过于简单的视角,有时候一个操作呈现出来的是幂等的,它的实现可以做任何事情但它需要是幂等的就可以(这再次提出了这个问题,那就是这种区分是否重要):
[…] 读的功能不一定是幂等的。“getNextIterator()”是一个读的功能,它不是幂等的,因为它会渐进迭代器。银行账号请求余额可能也不是幂等的,因为会创建审计日志。两个连续调用返回的结果可能是相同的(如果没有变化发生的话),但是日志条目是不同的。
Ashaf 回应了 Steve,他说幂等是服务的属性而不是接口的属性。
客户端需要从服务那里得到适当的响应,他并不关心服务是不是幂等的,是不是要记录日志。
那么,其他人怎么想的呢?对于幂等的含义,是否还有误解?如果一个被视为幂等的操作实际上会改变一些状态,如 Steve 所提到的更新日志,那么这有关系吗?
英文原文链接: What Is Idempotent in REST?
评论