SOA实践指南读书笔记
最近读完了《SOA实践指南——分布式系统设计的艺术 》这本书,以下是这次的读书笔记。
什么是SOA
在计算科学中,术语“面向服务的架构”(SOA)表达了一种软件架构的概念,它定义为使用服务来满足满足软件用户的需求。在SOA环境中,网络上的节点以独立服务的形式将自己的资源开发给网络上其他参与者,其他参与者按一种标准的方式使用资源。大多数对于SOA的定义都提到了实现SOA时使用Web Services(即使用SOAP或REST)。然而,可以使用任何基于服务的技术实现SOA。
……
与传统点对点架构不同,各种SOA都由松耦合、高度可以互操作的应用服务构成。这些服务基于某种格式定义进行互操作,该定义独立于底层平台和编程语言(例如,WSDL)。接口定义封装(隐藏)了供应商和语言相关的实现。SOA独立于开发技术(比如JAVA和.NET)。由此,软件组件将具备非常高的重用性,因为接口按与标准兼容方式定义。
基于SOA的逻辑架构模型
无状态服务为什么更好
首先,对于无状态服务来说,在服务层做到负载均衡和失效备援相当简单。ESB仅需实现能使用下一个最容易获得的服务实例即可。它甚至可以是其他消费进程以前使用过的线程。这意味着如果一个系统失败了,其他系统可以轻易地接手,继续工作。如果服务层的通量不够高,你可以将服务实例的数量翻倍,从而是通量翻倍。这意味着解决方案能线性伸缩,并且,如果一个服务实例意外死亡,其他服务实例能投入进来。
而有状态服务的缺点是,它们绑定在会话上。这样一来,知道消费者会话结束(或者连接失效)之前,为它们分配的资源都绑定在一个特定的消费会话上。除此以外,你必须能让ESB把连续的服务调用路由到同样的服务实例上。
然而,要注意的是,有状态服务仍然可以线性伸缩。使用一种“粘性”路由的政策,根据可用的资源,你在第一个服务调用被执行时找到你的服务实例。这一来,对服务实例数量的翻倍就又能将可能的会话数量翻倍了。然而,此时不具备服务资源可以在不同消费者之间共享的优点。并且,如果一个服务实例意外死亡,会话状态就丢失了。
同时也要注意,有状态服务可以有失效备援机制(并且仍然可以线性伸缩)。此时,每个服务器不但返回它自己的会话ID,还返回失效备援会话的ID。只要这个失效备援会话不在一个中央服务器上,而是在邻近的服务器上(并且,每个服务器的邻近服务器各不相同),这样就不会造成瓶颈。
等幂性
等幂性是服务的一种可能的属性。在服务的上下文环境中,它意味着对同一服务调用的多次递交/处理不会引发问题。假设你有一个向银行中增加钱的服务。如果一个消费者调用这个服务(例如,为了处理银行转账)并且没有得到应答,消费者不会知道服务的请求还是服务的应答失败了。例如下图中,服务可能在请求处理之前或之后丢失。
在后一种情况下,从供应者的角度来看,服务成功了,指定数量的钱被增加到了银行账户上。在前一种情况下,服务没有影响(即没有增加钱)。
当服务的消费者没有得到应答时,它不知道服务是否成功了。为求确保,它可能重试服务调用,再次发出同样的请求。结果是,达到供应者的服务请求可能有一次或两次。如果服务是幂等的,那么,服务请求到达多少次都没有关系:同一服务调用的多次请求只产生一次的效果。
刚刚描述的银行服务不是幂等的,因为,它依赖于第一次服务调用失败发生在何处,客户的余额可能最终被增加了两次,而不是消费者想要的一次。为了避免这样的问题,通常说来,只要有可能就是服务幂等是个好主意。
所有的读取服务都是幂等的,因为一个服务供应者无论多频繁地执行返回数据的请求都没有关系。然而,要注意,一旦做了类似每个请求都写一个备忘录项这种事,读取服务就变成了写入服务。如果这个备忘录和业务有某种关联,这就可能成为问题(例如,对同一样的事项,最终可能有多个备忘录项)。
写入服务可能是幂等的,也可能不是。当一个服务调用的影响依赖于后端现有的状态时,写入服务不是幂等的。举个例子,在前面的例子中,银行账户的最终余额依赖与最初的余额。如果开始的余额是500美元,服务调用增加100美元,结果将是600美元。如果因为最初的应答丢失了,另一个请求到达并且得到处理,最后的余额将是700美元。
一个幂等的写入服务的例子是,一个通过发送所有地址来设定客户地址的服务。供应者处理多少次请求都没有关系,结果将是一样的。
注意:你总是可以避免让服务不幂等。例如,我们可以修改银行服务语义,发送新值而不是应该增加的值,从而使服务幂等。这就是说,不是调用:
addToBalance(100);
我们可以调用:
setBalance(600);
这个调用可以反复地发送和处理,余额的结果都会是600。当然,其他服务也能在相同的账户中增加或减少资金,这就带来了问题。这就带来了问题。你如何才能知道最终的余额应该是多少呢?正如你看到的,从业务的角度来看,让服务幂等可能变得复杂。
除此以外,从业务角度来看,有的服务内生就是不幂等的。所有创建某些东西的服务都没有办法和一个状态去比较(除非你知道创建新资源的工厂的状态)。例如,如果你有一个创建银行账户的服务,你就是在创建账户,仅此而已。
如果从业务的角度来看,引入幂等性是个难题的话,那么,你需要一些对幂等性的技术支持。实现幂等性的普通方法相当简单,消费者发送数据的方式必须能让供应者看的出两个技术性的请求其实是同一个。为此,消费者可以随着每次新请求发送一个唯一的ID。如果消费者没有得到应答,它的每次重试都是同样的ID。下图是对此的图解。
在处理每个进入的请求前,供应者应将请求及相关联的唯一ID存储到一个中央数据库中。如果供应者收到一个ID已经在存储库中的请求,它就知道该请求已经被处理过。于是,供应者就不会再处理它,而是向消费者重新发送最初的应答(应答在第一次被送出去之前,也保存下来)。如果第二次请求到达是,服务实现正处于运行中,则供应者可以回复一个应答,说服务调用在运行中,或者供应者可以等到第二次请求处理完毕以后,再次把应答发送出去。
作为一项优化,消费者能和请求一起发送一个标志,这样供应者就知道此请求是否是一个重试。这样能做到改善性能,因为如果调用没有被标记为重试的华话,供应者就不必验证请求是否已经在存储库中了。
