在与许多客户的接触中,我发现有必要建立一套 SOA 的基本原则。下面的部分将介绍 SOA 中应有的基本原则。这些并非绝对真理,它们更像一个用于 SOA 相关讨论的参考框架。你会发现:前四项衍生自 Don Box 提出的四项原则,尽管随着时间的流逝,这四项原则的描述可能已经有了些变化。
1. 明确边界
服务被调用时,与实现其功能相关的内容都应被传递过来。对服务的所有访问都应该通过公共接口进行。调用服务时,非隐含的假设是必须的。“服务与消息紧密联系,因为参数进出服务的唯一方式是通过消息进行的”。作为通用的模式,服务调用不应依赖于共享的上下文,而应被作为无状态的模块。契约描述了服务的功能性与非功能性的能力和特点,管理着服务提供的接口。服务调用是一个具有业务逻辑效果的行为,可能有大量的资源开销,并且导致一系列不同于本地方法调用和远程过程调用的错误。服务的调用绝非远程过程调用。
服务的使用和提供应该尽可能地简单,因此与服务间的交互没必要被隐藏得太多。在SOA 中,服务发送和接收的消息、服务契约以及服务本身都应当是最好的构件。这就意味着,例如,被用到的编程模型和工具至少应该提供一个API,这个API 会帮助服务的编程人员了解上述概念。总的来说,一个明确的接口会封装服务的内在实现,而服务通过该接口发布自己的功能;与服务交互是一个具体的行为,它依赖于服务使用者和提供者之间消息的传递。
2. 共享契约和架构,而不是类
基于一份服务描述(一份契约),服务使用者和服务提供者都可以获得使用或提供服务的全部所需。根据松耦合原则,服务提供者不能依靠服务使用者来重用那些依赖于使用者环境的代码。毕竟,服务使用者可能使用不同的开发环境和运行环境。这条原则给 SOA 体系中所能交换的数据加上了严格的限制。理想的情况是,数据以符合一种或多种模式的 XML 文档形式被交换,因为这种方式可应用于任何你能想到的编程环境。
因此,因为这条原则在基于 DCOM 和基于 RMI 的环境中是不可能被遵守的,所以这两种环境基本上无法成为 SOA 的可用选项。
3. 策略驱动
为了与服务交互,必须满足以下两组不同的要求:
- 提供者提供的功能、语法和语义必须适应使用者的需求;
- 技术能力与需要必须匹配。
例如,一个服务提供者提供了能够精确满足用户需求的服务,但该服务是基于 JMS 的,可使用者只能使用 HTTP 方式(比如,服务被应用于.NET 平台)。服务提供者可能要求消息级别的加密采用 XML 加密标准,而使用者只支持采用 SSL 技术来保障传输层上的安全。即使在那些交互双方都拥有足够能力的案例中,它们的这些能力仍旧需要被“启用”。例如,提供者可能根据不同的使用者需求,对响应的消息使用不同的算法进行加密。
为了使尽可能多的形形色色的使用者能对服务进行访问,一种策略机制已经被作为 SOA 工具集的一部分引入了。在服务接口对功能进行描述的同时,策略对不同的,非功能性的能力和需求进行了指定。(译者注:策略指定的是服务之外的补充信息,是对服务使用者提出的特征要求)
4. 自治
与明确边界原则相关,服务自治意味着,接口成为服务与外界联系的唯一方式,至少从 SOA 的角度来看是这样的。需要注意的是,服务的运行环境一定是可变的。例如,在丝毫不影响使用者的情况下,就可以从轻量级的原型实现转换到成熟的、基于应用服务器的协同组件集。服务能够被彼此独立的修改、部署、发布新版本和管理。服务提供者不能寄希望于服务使用者,期望它们依靠自己的能力迅速适应新版本的服务,有的使用者可能甚至没这个能力或者根本不愿去适应新版本的服务接口(尤其是当这些服务接口超出了服务提供者控制范围的时候)。
5. 采用可传输的协议格式,而非 API
服务通常采用协议格式来发布,协议格式应该是明确的、可传输的并且被服务所支持的。这一点与前两条原则非常相关,但却带来了新的见解:为保证一个服务最大程度的可访问性(及长期的可用性),只要交互过程遵守为该服务定义的策略,那么由任何依照服务接口进行消息交换的平台都可以访问该服务。例如,通过以这一原则来测试主流的动态编程语言(如 Perl、Python 或 Ruby),我们可以去考虑该语言能否使用或提供一个特定的服务。虽然,在现有的技术实现里,这条原则可能还没有发挥作用,但这个思路可以作为下列准则的试金石:
- 使用开放的标准或者可阅读的描述来描述所有消息格式。
- 不需要特定的资源就可以创造出符合这些合理的模式的消息。
- 成功通信所必需的附加信息,例如包含安全性或可靠性约束的头信息,它们的语义和语法要遵循公开的规范和标准。
- 服务交互时所使用的传输(或传递)协议中至少有一个是标准的网络协议,或它可以通过标准的网络协议来访问。
6. 面向文档
服务交互时,数据是以文档的形式来传递的。文档是一个被明确模块化的,有层次结构的数据容器。面向文档的一个重要特征就是自描述。最理想的情况下,文档是对现实世界中的文件(如订单、发票或帐单)的建模。文档应该被设计来确保它在问题域的上下文中发挥作用,这意味着它们可能应用于一个或更多的服务。
与现实世界的纸制文档相似,和服务交换信息的文档将包含冗余的信息。例如,文档中可能同时包含了客户 ID 和客户地址信息(尽管客户 ID 可能已经足够了)。这种冗余是可以接受的,因为它将服务使用者和提供者双方的服务接口和隐含数据模型隔离开来。应用面向文档的模式的同时,服务调用成为有意义的业务逻辑消息的交换,而非上下文无关的 RPC 调用。虽然通常可以认为 XML 将被作为服务文档的格式和语法,但它还没有成为标准。
在一个 SOA 连接中,参与者之间的消息流转于不同的系统,使得各个系统之间彼此独立。松耦合原则要求参与者对共知的依赖越少越好。当消息在分布式对象或 RPC 基础架构中发送时,客户端和服务器端使用由同一个接口描述文档生成的代理类 (stub 和 skeleton)。如果不是这种情况的话,当契约不支持双方的交互时,通讯就会停止。因为这个原因,RPC 风格的基础架构要求客户端和服务器端程序代码的同步运行。
下面将通过比较来说明这个问题。请仔细思考下面的消息:
2006-03-1347113
与之相比:
<order></order><br></br><date></date>2006-03-13<br></br><product-id></product-id>4711<br></br><quantity></quantity>3<br></br>
显然,第二种选择更具有可读性,第一种则不然。还值得注意的是,在第二种情况下,使用诸如 Xpath 技术访问信息的参与者,与采用固定的语法的参与者相比,将会更好的远离更小的,非破坏性的变化。反之,如果在使用诸如 stub 和 skeleton 之类的 RPC 模式的同时,采用如 XML 这样的自描述性消息格式,就只会使 XML 落得挥霍带宽的恶名。如果使用 XML,就应当发挥它的优点(参见这篇文章,文中深入剖析了现有许多 Web 服务技术簇在 XML 应用的测试中失败的原因)。 ### 7. 松耦合
多数 SOA 的倡导者都认为松耦合是一个很重要的概念。不幸的是,对于究竟哪些特征造成一个系统松耦合,有许多不同的看法。一个系统可以在多个维度表现为松耦合或紧耦合,它依赖于具体的要求和上下文,系统可能会在一些维度是松耦合的,在另一些维度是紧耦合的。这些维度包括:
- 时间:当参与者在时间上是松耦合时,它们不需要在同一时间启动并进行通讯。这要求两者之间采用某种缓冲或队列机制,尽管这种机制与松耦合无关。当参与的一方向另一方发送消息时,交互的继续不依赖于逻辑上或物理上能否立即返回应答消息。
- 位置:如果一方参与者查询与之通信的另一方参与者的地址,另一方的地址可以透明地进行变更,不需要重新编程、重新配置或者甚至不需要通信伙伴的重新启动。这意味着查找(lookup)过程采用某种目录或地址来存储服务终端的地址。
- 类型:同静态与动态,弱类型与强类型这些编程的概念类似,参与者既可以全部依赖也可以部分依赖文档结构来实现它的功能。
- 版本:参与者可以依赖服务接口的特定版本,也可以兼容某个范围内的版本。所需匹配的版本越确切,参与者在这个方面上的松耦合性就越差。一个好的原则是遵循 Postel 法则(译者注:Postel’s Law——“Be liberal in what you accept, and conservative in what you send.”):服务提供者应尽可能兼容许多不同的版本,这将使它更加健壮(可能甚至需要容错),服务使用者应尽可能遵循精确的语法和文档类型。这将增加整个系统的稳定性和灵活性。
- 基数:服务消费者和提供者可能是 1 对 1 的关系,尤其是在请求或响应交互发生时,或队列被明确使用的情况下。在别的情况下,服务使用者(在这种情况下,称作“消息发送者”或“事件源”更为合理)可能既不知道也不关心有多少人接受了消息。
- 查找(Lookup):参与者打算调用服务时,既可以依赖服务提供者的物理名或逻辑名,也可以先通过一组功能描述来执行查找(lookup)操作。这意味着存在一个注册表和(或)仓库,对存储其中的使用者需求和提供者能力进行直接或间接的匹配。
- 接口:参与者可能要绑定到一个特定的服务接口或是支持一个通用的接口。如果使用通用接口,所有该接口的使用者都能与所有该接口的提供者进行交互。尽管可能乍看起来这有些笨拙,但单一通用(统一)接口的原则就是 WWW 架构的核心。
创造一个满足以上所有维度的松耦合系统,既不可行,也没必要。不同类型的服务要做不同的取舍。Carlos Perez 的经典之作中 (如这里和这里) 有更多的关于松耦合各个维度的讨论。
8. 遵循标准
一个 SOA 应用中应遵循的一个关键原则是,信赖标准而非专有的 API 和格式。标准存在于技术方面,如数据格式、元数据、传输协议;也存在于业务层面,如文档的类型。(例如, UBL 中所提到的那些)(译者注:UBL 定义了业务文档的通用 XML 库,UBL 的文档类型包括订单、发票等)
很显然,一些人认为专有的解决方案,如一些 EAI 或消息服务提供商提供的方案,都遵循 SOA 原则。这个原则不遗余力地强调标准的重要性。当然,由于有太多可供选择的标准,什么情况用何种标准成了颇具争议的问题。标准的一个重要方面是它的可接受性(在Web 服务的标准中,基本上可以认为“Microsoft 肯定要插上一脚”)。
9. 独立于软件供应商
任何架构性的原则都不应依赖特定供应商的产品。将抽象的概念转化为具体的,可运行的系统的过程中,不可避免的要决定使用何种具体的产品,包括商业的或者免费开源的软件。这些决定都不应影响架构层。这就意味着要尽可能的依赖互操作性和可移植性的标准。因此,要应用支持适当标准的技术来构建服务提供者和使用者,不要受限于任何软件供应商的技术路线。
10. 元数据驱动
SOA 中所有的元数据对象都需要被按照一种方式储存起来,这种方式将确保元数据对象能够在设计和运行时被发现、检索和解释。元数据对象包括对服务接口、参与者、端点和绑定信息、组织单元和职责、文档类型或模式、使用者或提供者关系等的描述。这些对象的用途应当是被代码自动生成或者解释,成为服务和参与者生命周期的一部分。
以上是我的原则列表。 即使你不完全同意——而坦率地讲,我也不希望你完全同意, 至少不是全部都同意——我希望你能带着它们来引发一些讨论!
评论