最近几周,许多业内分析师都在鼓吹关于 SOA 的恐惧、不确定性和怀疑(FUD)。例如,Gartner 声称,打算启动SOA 项目的公司正在减少,而不打算启动SOA 项目的公司则在过去12 个月内由6% 增加到了16%。这些说法和文章不由得让人觉得公司不再相信构建可在不同解决方案中组合使用的可重用IT 资产了。
对于SOA 兴趣的降低,我们认为的解释完全不同:SOA 失败的一个关键因素恰恰是无法生产出可重用和可组合的资产。每个新消费者对服务提出的要求似乎都和服务现有功能完全不同。鉴于服务比更传统的解决方案架构的设计、构建和运营成本要高,这些服务要是不能被重用的话,SOA 就无法实现它的大部分业务场景。依我们的经验看,能超越服务的原始重用从而获得SOA 所承诺结果的公司寥寥无几。
如果你曾期望重用服务,那么对于表达服务提供什么和它如何被消费的契约,你必须要拥有一个清晰的设计指导方针。没有太多的SOA 治理,你可能也能启动 SOA 项目,但是要是缺少契约设计的指导方针,那就大错特错了。随着时间推移,这些设计指导方针无疑会成为SOA 治理设计的监察策略的核心部分。然而,整个业界似乎已经着重强调将SOA 治理流程作为成功构建可重用资产的方法,往往对“只是一组杂乱无章的Web 服务”(JaBoWS)方法不予考虑,可是这一策略却不足以证明它能取得这一特殊目标。
我们认为SOA 治理是必要的,但是它通常无法交付能达到长期重用资产所要求水平的服务规范。SOA 治理是重要的,而且在识别、详述和设计服务时应该尽量地在实践中使用治理。但是,由于有限的资源、时间和无法很好地预测未来3-6 个月的情形,单靠治理无法创建出可重用资产。新的、不可预测的服务消费者不可避免将提出新的需求,迫使现有服务进行演变。要是缺乏正确的版本管理策略,服务的新版本将导致全新的服务,造成相似的需求,却又独立的注册项、代码库和生命周期。缺乏正确版本管理策略的组织,往往会在生产环境中部署、运营和维护相同服务的几个“版本”,从而极大地制约SOA 的收益和投资回报,并为某些鼓吹“重用”不应被期望为SOA 项目的一部分的分析师增添注脚。
在本文中,我们为建立一个适合服务重用、可组合性和兼容优先的消费者(或提供者)的服务契约版本管理策略提供了一系列推荐实践。我们认为,这个版本管理策略对于取得满意的服务重用水平并进而产生SOA 项目更高(且是预期的)的ROI,是必要的。
契约的元素
“契约是SOA 中最重要的元数据”, ZapThink 的分析师 Ron Schmelzer 如是说。服务提供者和服务消费者之间的契约存在很多部分。使用诸如 XML 模式、WSDL 和 WS-Policy 这样的技术,这些元素中的某些可以被设计成机器可读的,甚至其中某些是运行时可执行的,而另一些元素则仍然还是主要为人类消费而服务。
这篇论文的目标绝不是从格式和穿行于连接之上的比特序列,到服务水平协定或法律条文(如关于个人隐私)等方面检查契约的每个可能元素。相反,我们重点关注一个简单问题:定义一种策略,使其不仅支持服务提供者或消费者演变,同时还依然兼容现有的服务提供者或消费者。我们认为这个策略是交付 SOA 收益的核心,并能极大减少构建服务的初始成本(因为它减少了治理的需要)和持续维护及运营服务的成本。
我们建议的版本管理策略有两部分:
- 创建机器可读的契约元素,它们表达了在运行时由服务消费者和提供者执行的所有规则
- 在服务消费者和提供者一致认同的契约元素各版本之间,创建或利用一组兼容性规则
机器可读的契约元素是版本管理的基础,因为这些元数据可以且必须被服务的消费者和提供者同时使用,以便确定当前在某个端点被发送和被接收的消息是否可被正确处理。这些元素为潜在地成功处理一条已知消息设置了最低标准。当然,如果不满足的话,服务实现中的业务规则可以潜在地返回一个契约异常。
一种策略可能是定义机器不可读契约元素,并且每当消费者或提供者无法处理消息时就始终保持交换异常,直到消费者或提供者修正问题为止。例如,REST 社区的成员就鼓吹“统一契约”的采用排除了其他机器可读元素的需要,“统一契约”不仅能够以其他方式提供无二义性的语义,而且还能有助于服务的实现、验证或配置。但是,REST 社区并没有提供RESTful 服务能够以一种兼容方式演变的证据。
问题的重点并不在于需要哪些契约元素,而在于如何方便地表达服务提供者(或消费者)的两个版本之间的兼容性。换句话说,如果消费者与服务提供者的新版本进行交互时,我们是否能预期其行为等价于前一个版本的行为?采用“统一契约”,如对发生在某一特殊位置的消息收发简单地达成一致,将不会对回答兼容性问题有所帮助。
在这篇文章中,我们将把重点放在那些对于验证传入的SOAP 消息是否合法所必需的元数据上面。因此,我们将关注于定义在 WSDL 文档中的契约元素。鉴于 WSDL 2.0 的使用并未普及,我们将使用 WSDL 1.1。我们相信,将这一策略应用于 WSDL 2.0 上时,并不需要进行重大的修改。 WSDL 1.1 的元素包括:
- 服务定义的目标名字空间
- 消息类型
- 消息定义
- 错误
- 端口类型
- 绑定
- 服务定义
- 使用 WS-Policies 表达的可选策略
图 1. WSDL 1.1 契约的元素
版本管理指导方针
服务和模式的版本管理可以导致下列兼容性场景的出现(图 2):
- 无兼容性
- 向前兼容
- 向后兼容
如果新版本契约继续支持针对老版本契约而设计的消费者,它就被认为是向后兼容的。
如果契约被设计成可支持未来未知的消费者,它就被认为是向前兼容的。这类契约预料到了消费者会随着时间而不断演变,借助 XML 模式的通配符实现了这种扩展性。这种兼容性往往会在 B2B 场景中找到,其中一个消费者会和多个服务提供者协作,而消费者则可能会以非常缓慢的速度发生演变。在企业中,最常见的场景是向后兼容性。
通常所说的“兼容”就是指向后兼容性,而且在对契约进行非破坏性变更时,它是有可能做到的。对契约进行破坏性变更总会造成不兼容。这些场景已经被广泛地描述过了,例如你可以在 John Evdemon 或 Dave Orchard 的文献中找到。
注意,契约可以同时具备向前和向后兼容性(详见后续讨论)。
图 2. 服务操作的兼容性场景
Web 服务对向前和向后兼容性场景的支持在分布式计算技术的世界内是无可比拟的。 RPC、CORBA、DCOM 或 JEE 都无法开箱即用地支持这些场景,而只能靠应用特定的设计约束。事实上,对这些场景的支持能力是实现松耦合和重用的关键。
当服务以向后兼容方式演变时,它可以在无需变更现有消费者的实现或配置的条件下被更多的消费者重用。当治理没有预测到未来消费者的需求或当预算使得在初始版本中无法实现所有所需的特性时,兼容的服务版本管理都可以在不影响现有消费者操作的情况下实现更新。这正是“JaBoWS”能得以缓慢但肯定能演变成企业级服务的方式,最终为广泛的消费者提供服务。
规定的版本管理模式将兼容性定义如下:
- 主版本(Major versions):不兼容(破坏性变更)
- 副版本(Minor versions):兼容(非破坏性变更)
破坏性变更是指那些将导致传入消息处理失败的现有模式类型的修改,如将一个现有的“可选”元素变成“必需”或增加新的“必需”元素。而增加一个新的“可选”元素则是非破坏性变更。模式或服务的非破坏性变更被注册为副版本,而破坏性变更将总是注册为新的主版本。
一个服务的所有支持版本管理(Versionable)的制品都将受变更的影响,这些变更将通过这些制品自底向上地进行传播,最终的连锁反应将影响到你的消费者。这种连锁反应的细节可参见表 1,图 3 给出了它的图解。在版本管理让你控制变更影响的同时,兼容性将帮助你减轻版本管理的某些负面影响。
图 3. 变更的连锁反应
在下表中,我们将规定:使用主版本的服务是不兼容的,使用副版本的服务是兼容的(向前或向后)。微版本(Point version)应该被用来表示那些不涉及契约变更的版本,只限于实现、部署或配置变更(如修正错误、发布新的服务容器等)。由定义可知,微版本总是兼容的(附注:这只是规定,而非保证)。
制品 变更 主版本 副版本 微版本 服务 破坏性 X 服务 非破坏性 X 组合服务 不兼容模式服务 X 组合服务 兼容服务 X 服务 不兼容模式 X 服务 兼容模式 X 模式 破坏性 X 模式 非破坏性 X 模式聚合 不兼容模式 X 模式聚合 兼容模式 X 代码 修正错误 / 维护 X 代码 安全修改 X 代码 语义 / 不安全的修改 X 其他服务制品 任意 X 表 1. 代码、模式和服务变更的版本管理连锁反应
在设计消息类型模式时,使用以下简单规则的两个消息类型可被认为是兼容的:
- 对于已知消息类型和已知主版本来说,XML 名字空间的值必须不变。
- 消息类型模式必须在消息类型的根元素上包含强制的副或微版本自定义属性,使用如 xsd:int 类型。
- 不论是否使用 XML 验证,每个消息消费者必须验证传入消息的主版本是否匹配它的实现版本。当消息发送者和接收者的主版本不匹配时,应该返回一个异常。
在.Net 世界,消息类型制品也被称为数据契约。
Web 服务扩展性的指导方针
向后兼容性的原则已有很好的文档化(也请浏览这个参考),但是它们却鲜有应用。为了清楚起见,我们在此对它们进行了详细说明。
服务定义的目标名字空间 WSDL 的目标名字空间可以不同于消息类型XML 模式的名字空间。每次修改服务契约或实现时,WSDL 的目标名字空间都必须是不同的,这适用于微、副或主版本。
假若SOAP 动作(SOAP Action)是手工定义而非由运行时自动定义的,这个名字空间不会在运行时被使用。因此,所有WSDL 定义必须包含手工定义的SOAP 动作 [1]
消息类型 消息类型的模式必须借助 XML 模式扩展性机制进行设计(我们将在向下文详细讨论这些机制)
消息类型的名字空间必须只引用服务契约的主版本,以表明一个不兼容的服务版本。副版本可能作为根元素的属性被指定
消息定义 可以给消息定义潜在地增加多个部分,但是我们推荐使用一个部分来包含消息信封,在其中使用 XML 模式的扩展性规则来扩充内容
错误 不能给现有操作增加错误,除非它们属于新的副版本相关的消息类型扩展
端口类型 端口类型可以使用新操作来扩展 不能变更操作的签名或操作调用的顺序(WSDL 并没指定这一信息)
绑定
可以定义新绑定,但是不能修改现有绑定,包括服务的端点
服务定义
服务定义只能使用新端口扩展,现有端口不能修改
使用 WS-Policies 表达的可选策略
对于策略,缺乏可用来评估兼容性场景的通用框架表 2. 服务定义的兼容性设计规则
为了实现向后兼容性,必须仔细规划使用 XML 模式的消息类型的设计,也就是所说的 XSD 扩展性。
XML 模式标准引入了 XSD:ANY 作为通配符元素。 XSD:ANY 让模式可以以一种定义良好的方式进行扩 展。 XSD:ANY 包含一个名字空间属性,该属性要么约束要么扩展那些可能出现于该通配符中的元素范围。名字空间属性可以被设置成以下任意一个:
- ##any,可以用来自任意名字空间的元素扩展模式。
- ##targetnamespace,限制通配符只能是那些出现在目标名字空间的元素。
- ##other,使用来自目标名字空间的元素扩充模式是非法的。
processContents 属性指明了解析器验证模式扩展的方式:
- strict,要求解析器验证所有模式扩展。
- skip,不对模式扩展进行验证。
- lax,对来自所支持名字空间的元素进行验证,忽略那些未知或非预期的元素(绝大多数 Web 服务规范都使用 lax)。
关于 XSD 可扩展性,在 XML 模式 1.0 规范中存在一个问题。由于唯一词缀属性(UPA)规则,如果待验证的 XML 模式类型的类型定义中最后一个元素是可选或未限定数目的元素,那么会出现二义性的问题。鉴于这个原因,当这个类型的最后一个元素具有可变基数时,在使用元素之前,我们必须增加一个基数为 1 的特定元素。
例如,你可能期望选择这样一个元素:
在 XML 模式 1.1 版中,将消除这种二义性,这样也就不再需要这种附加的元素了。
使用 XML 模式扩展性特性只是事情一方面,我们仍然需要定义和认同一系列的规则来解决老版本消息消费者遇到新版本消息的问题。这个新消息很可能会包含属于原始消息模式某个扩展区域的元素。这时这个问题成了:我们该拿这些元素怎么办?我们推荐应用下列规则,它将产生无需在消费者方进行变更的向后兼容性:
- 服务遇到未知元素的行为必须被扩展性处理规则作为契约的一部分定义
- 给针对向后兼容而设计的消息类型新版本增加的新元素不能使消息类型的早期版本无效
- 消息消费者必须接受并且不去处理任何它们不认识的元素。
- 一个包含相同类型作为其本身请求的请求所产生的响应,必须增加它们在处理请求之前移除的相应元素
这些规则确保了每个消费者能正确地针对其在实现中一定要处理的那些元素使用 XML 模式验证。我们决不推荐马虎地使用 XML 和 XML 模式扩展性,同样 也不推荐不进行验证。(图 4)。与业界广泛相信的相反,XML 和 XML 模式扩展性是以兼容性为基础的版本管理策略的关键,因而也是实现重用的关键。可是造化弄人,这种关键的技术优 势居然常常被大多数业界专家和分析师低估。
就实现而言,消费者和提供者的实现是不对称的:
- 如果消费者验证提供者发送的传入消息,它们将通过验证(由于定义的实现规则)
- 然而(并且肯定),服务提供者实现一般必须了解传入消息的所有副版本的模式,并基于已知主版本的副版本号验证每条传入消息,甚至有可能将调用路由到这个实现的变种
这种做法的原因是因为副版本较高的模式无法验证副版本较低的模式(一般而言)。在相同目标名字空间中,高副版本很可能会包含无法用低副版本模式验证的“必需”元素。
消费者变量
在 XML 模式中,还需要有不属于版本管理范畴的“扩展区域”(图 4)。例如,在需要消费者相关的变量时(即消费者和提供者之间独立于其它消费者、特殊的关系),扩展就很常见。这些扩展可被视为特殊消费者和服务提供者之 间私有的契约,内嵌于所有消费者共有的契约中。同样,有一个支持这些扩展的契约是实现高水平服务复用的关键。
- 对于扩展的处理必须独立于一般的版本管理模式。我们推荐在消息类型的根元素之下,使用单个元素实现扩展。
- 扩展不能被消息消费者处理,除非它们被明确定义在消息消费者的消息类型中,或者除非消息消费者指定了 mustUnderstand=‘true’属性。如果无法处理所需元素,消息消费者实现必须产生异常。
- 建议将扩展包含在不属于消息类型根元素的名字空间内。
图 4. 版本管理和使用者扩展的兼容性
使用 Web 服务技术,开发者可借助不同版本的业务逻辑来使用不同端点,访问相同信息,这极大简化了相关服务的维护和运营。总的来说,Web 服务技术、XML 和 XML 模式不象之前的分布式计算技术,为支持兼容性场景提供了空前的机会,包括 REST 也无法依赖一个稳定契约来指明版本管理策略。 在 REST 中,每个资源(实例)暴露一个端点的事实在端点和资源(实例)之间造成了强耦合,这增加了管理业务逻辑实现多个主版本的操作难度,因为 REST 在两者之间并未引入一个间接层。资源和端点之间的这种耦合,使得给资源类型分配一个版本很难,因为在 REST 中,资源类型并不存在,而且每个资源(实例) 能潜在实现与 HTTP 动词及任意名词特定版本相关的其自身版本的业务逻辑。
兼容性和可组合性
上文定义的兼容性场景可以与 Thomas Erl 系列最新书籍《SOA 的 Web 服务契约设计和版本管理(Service Contract Design and Versioning for SOA)》中所定义的策略相结合:
- 严格(Strict):任何变更都被认为是不安全的,必须产生新版本。非破坏性变更产生新的副版本,破坏性变更将要求新的主版本。向后和向前兼容性都有意地被忽视了。
- 灵活(Flexible):非破坏性变更只产生新的微版本,破坏性变更当然将要求新的主版本。这一策略使用向后兼容契约,但不使用向前兼容契约。
- 宽松(Loose):同时使用向后和向前兼容契约。破坏性变更当然将要求新的主版本。
“严格”是非常普遍的模式版本管理处理方法。它是一种安全的方法,在变更契约时不会给你带来意想不到的后果。事实上,以上显示的模式制品版本管理表 格正是以“严格”策略为基础的。但是,随着服务的演变,该方法会造成契约版本的爆炸,这将损害可发现性,尤其是治理,更不消说重用。此外,为了让服务发挥 作用,将需要保持服务的多个版本在线运行。变更在聚合模式(模式组合)中所使用的模式将波及所有相关聚合,造成新模式版本的多米诺效应。并且毫无疑问,一 旦服务使用的模式发布了一个新版本,该服务必须也发布一个新版本。这时,涉及这些服务的组合服务将同样受到影响,必须也发布一个新版本。因而,连锁反应比 你想象的要大得多。
这种版本多米诺效应很快就会导致波及系统中所有支持版本管理的制品的一系列变更。而且它的影响并未就此止步,它最终将会影响到你的消费者。
如果你的服务发现能力很差且服务版本呈扩散趋势,那么你的可组合性将会受到损害。倒霉的消费者应该使用哪些服务,哪些服务又将作为组合服务进行合作?在标准化服务和模式有上千个版本的情况下,仅拥有包含领域公共信息模型的标准化服务契约是不够的。
“灵活”试图通过将所有非破坏性变更都视为安全和向后兼容的来减轻“严格”的版本爆炸效应。因为有意以向后兼容方式进行变更的契约仍将继续支持针对 老版本契约而设计的消费者,故而并不需要新的版本——即它只是一个微版本。服务可以轻易地被组合在一起,因为一个主版本内的所有契约都是向后兼容的,并且 每个主版本只需考虑一个副版本——最新的那个。结合“宽松”策略将可获得向前兼容的契约,并且所有你的兼容性和可组合性问题都成为历史。
理论上这似乎是一个完美的解决方案,只有破坏性变更需要新版本,波及所有支持版本管理的制品。理论上,它不太可能有“安全”变更的副作用,不论是功能的还是非功能的。正如 Nicolai M. Josuttis 在《SOA 实践(SOA in Practice)》一 书中所说,就算是增加一个新的 XML 可选元素都会有非功能的副作用,如增加服务的响应时间,会破坏服务的 SLA。更安全的做法是提供一个具有新模式的服务 新版本,就好像出现了问题一样,这样就只有因这个变更而需要升级的消费者受影响。但是要记住,在我们建议中,可组合性是一种规定,而不是必需具备的。因 此,如果 SLA 由单个元素改变了,这就必须定义一个新的主版本(及 XML 名字空间),即便从 XML 扩展性角度来看这种变更具有完美的兼容性也一样。
它们中的某种组合会给我们带来更好的体验:
- 灵活 / 严格:对于所有安全的模式变更采用“灵活”,而对任何不安全的模式修改采用“严格”。破坏性变更将要求新的主版本。这种方式支持向后兼容性。
这种“灵活 / 严格”策略组合了三种原始策略中最好的部分。它将变更划分为安全的和不安全的,即便它们在理论上是向后兼容的(即非破坏性的)。安全变 更将产生微版本,而不安全变更将至少产生副版本。对于辨别哪些模式是安全的和哪些是不安全的,并不存在严格的分类方法,但我们建议:给模式增加内容被认为 是安全的,而修改现有模式的内容则是不安全的。就是对“安全”变更也总要判断它是否会导致负面影响,记住要考虑非功能性方面。
策略 变更 主版本 副版本 微版本 FW 严格 破坏性 X 严格 非破坏性 X 灵活 破坏性 X 灵活 非破坏性 X 宽松 破坏性 X X 宽松 非破坏性 X X 灵活 / 严格 破坏性 X + 灵活 / 严格 非破坏性、安全 X + 灵活 / 严格 非破坏性、不安全 X + 表 3. 兼容性变更被分成安全的或不安全的
使用“灵活 / 严格”将影响模式制品的版本管理表,因为所有处于“严格”程度、必须产生一个新副版本的变更,现在成了只是安全变更的一个微版本。不安全的非破坏性变更仍然必须产生一个新副版本。注意,即使你断定一个代码变更是安全的,我们仍推荐将其产生一个新副版本。
模式的验证和路由是基于它们的主版本和副版本,从而以微版本为目标的兼容性也将减少服务中错综复杂的版本管理机制的需要。
我们建议,对于你发布的服务使用“灵活 / 严格”策略,并且以规划扩展性的形式使用向前兼容性。避免单纯的到处插入模式通配符,因为它们将导致模糊的 契约,有损于发现能力。John Evdemon 建议:“模式应该是针对扩展性而设计,而不是为了避免版本管理”。明智地使用无二义的通配符能帮助我们最小化服务版本的数目,我们强烈建议 遵照这些指导方针。
使用兼容性契约和多个活动服务版本的组合可以确保系统的灵活性足以适应随时间发展、不可避免发生的变化。简单行事。不论你做什么,都不要试图在服务 中实现某种隐式、魔法般的版本管理自动处理策略;取而代之,将服务在抽象端点暴露出来,并应用智能路由来实现服务的虚拟化,在服务之外应用模式的 Duck-Typing。
在开发中你应该使用“灵活”策略,因为在该阶段需要的是机动性。待服务已经发布之后,再应用“严格”的版本管理,要不然你最终只能严重地伤害消费者和提供者开发人员的感情。
注意,不同平台对向后兼容性的支持是不同的。一些要求使用模式通配符,而另一些(如 WCF)则已经隐式地支持了向前兼容性。对于与你的向后兼容模式一起协作的相关平台,要总是进行测试。不要依赖其只是工作就行了,互操作性对可组合性也很总要。
数据模型和消息类型 DSL
分布式计算技术的一个核心问题是,既处理信息,又要处理业务逻辑。一些方法擅长管理分布式信息(REST/HTTP),而一些技术的强项是调用业务 逻辑(Web 服务)。那些尝试结合使用远程技术和命名 & 身份服务来解决这两个问题的技术一般都无法交付一个信息访问和业务逻辑调用和谐共处的环 境。绝大多数时候,像数据传输对象模式这样的模式已经成为交互的普遍模式,信息表示只是简单地在端点表示服务(即业务逻辑)之间来回地被传递。
针对该疑难问题解决方案的缺乏,其根本原因之一在于一个事实,即企业数据是“关系型的”并且往往利用了双向关联。相比之下,网络则建立在“导航型” 数据模型和单向链接之上。在这两种情况下(导航型和关系型),通常都需要以非规范化(denormalized)的方式经由相同端点获取数据。这就意味 着,在获取采购订单时,还需要返回一些客户相关信息(姓名、地址、电话……)以及配送信息。不论你使用的是 RESTful 方式还是 Web 服务方式,问题都 完全一样。当然,REST 要稍好一些,它为这个问题提供了一种规范的(normalized)解决方案,因为指向客户的链接能够嵌入到采购订单的表示中, 但是这通常并不实用,因为表示通常需要包含关联数据,增强用户体验,限制导航和网络来回传递数据。
这篇文章的目的不是为了解决这个分布式信息集成的问题。我们将假定:在 REST 和 Web 服务世界中,人们只能以某种方式在端点之后处理它(这正是我 们当前所处的现状,直到分布式计算技术有了更多进步之后,该状况才会有所改观。)。在这样一个环境下,人们需要处理的是如何管理非规范化的消息类型,在版 本管理的环境下(即在企业数据模型需要一个修订版的情况下)尤其如此(图 5)。
图 5. 如何保持企业数据模型和消息类型的同步?
直到现在,业界仍然还是让企业数据模型、XML 模式和消息类型处于相对独立的状态。人们最多定义一个具有双向链接的本体(ontology)。
在这篇文章中,我们认为(图 6):
- XML 模式不应该被用于创建和管理企业数据模型。
- 应该基于 DSL(EDM-DSL)来创建和管理企业数据模型
- 应该基于 DSL 来创建和管理消息类型,同时把企业数据模型元素当作构建单元(MT-DSL)
- 应该从消息类型 DSL(其本身引用了企业数据模型的元素)中创建 XML 模式。
图 6. 企业数据模型和消息类型 DSL
让我们依次探讨这些问题。XML 模式不应该被用于创建和管理企业数据模型,因为 XML 模式从来都是不为此而设计的。XML 模式不能够有效地描述企业 数据模型的关系性质。XML 模式本质上是层次型的,不能很好地建模信息实体之间的双向关系。XML 模式技术非常适合描述和验证独立“文档”的结构,如消息 类型、Web 页、Office 文件……
企业数据模型应该基于 DSL 被创建和管理,因为现成的建模技术——无论是 UML 还是实体关系图(ERD)——都没有拥有描述企业数据模型的合适语 义。核心语义的缺乏以及对现有技术而言的不切实际,使得这些建模技术根本就没有能力描述相关元素的层次结构。换句话,无论 UML 或 ERD,我们都没有用来 定义采购订单或客户边界的语义。我们能够定义类或者实体,但现实中的采购订单或客户是几个类的有效“组合”。我们需要能够定义这些对象的边界以及支持这种 语义的非“标准”建模语言,因为它们本身跟物理实现模型很接近(对 UML 而言,是面向对象;而对 ERD 而言,则是 RDBMS)。在 UML 情况下,我们必须 定义一个 UML profile 用来扩展这些语义。但我们推荐创建一个专用的数据模型 DSL 来代替 UML profile 的使用,因为借助 UML 元数据,转换要更容易且更整洁。
应该基于专用的消息类型 DSL 来定义消息类型,该 DSL 引用了企业数据模型的元素,因为,本质上,这正是消息类型实际的构造方式(或至少是应该被构 造的方式)。当你创建一个服务接口时,无论它是基于 RESTful 的还是基于 Web 服务的,你其实都想要创建一个既从语义上又从结构上反映企业数据模型的 消息类型(或者资源表示),而不是反映已知后台系统的特征。这与提倡最小化“契约和实现”之间耦合的松耦合实践不谋而合。可想而之,基于企业数据模型语义 的方法可以同时减少治理(或至少是重用数据治理的工作量)和创建服务接口或资源表示(它们极有可能是可重用的和可组合的)的需要。
最后一个问题,扁平的 XML 模式应该从这些消息类型定义中生成(它们自己建立在企业数据模型之上)。扁平模式改善了 Web 服务组件之间的互操作性, 因为有些组件对于处理复杂、嵌套的模式文件有困难。XML 模式是验证这种结构和这些“文档”(某些)内容的理想技术。对于描述结构和验证传入消息的内容而 言,它仍然应该还是一个主要的选择。
活动服务版本
实现服务生命周期管理策略和有助于治理服务版本的过程非常重要。我们推荐制定一个“活动服务版本”策略,以保证你必须治理的服务版本的数目为最小。但是,就我们的经验来看,这一策略很难坚持,因而,将这个策略实现成为一个指导方针,而非一条法律。
这一策略一般是指,对于消费者而言,包括前一个主版本的一个隐藏活动副版本在内,一个服务应该最多只有 3 个活动版本可用。隐藏的副版本给当前消费者 回退到一个兼容版本留出了过渡周期,以防发生为最近一次副版本做出了不正确的兼容性声明。在这种情况下,消费者将手动地指向前一个副版本的端点。
图 7. 服务版本划分
通常情况下,你可能最终会出现有某些服务要求 5~6 个主活动服务版本的情况,对于被多个消费者使用的流行服务而言尤其是这样。总会存在有某些消费者 落后于新版本的升级,因此你可能不能把那些旧版本给停用掉。但是,我们推荐的方法确保了它对于每个主版本是可用的,我们只需要分别暴露最近的副版本即可。
确保新消费者总能缺省地发现你服务发布的版本(即最新的主 / 副版本)。发布的版本就是开发者使用经典的 http://url/?WSDL 来“发现”服务的那个版本。此外,我们推荐在你的服务注册中心中可以发现服务的 3 个主版本。它们每个都必须是最新的副版本。
最后,你必须监视服务的使用,以便了解“谁使用了哪个服务”。服务生命周期管理员必须通知所有消费者“版本已过期”并督促他们升级到一个活动的服务 版本。一旦某个过期版本不再有人使用,你就可以使其退役了。这样,服务版本的生命周期管理就结束了。可想而知,要是他要同时为主版本和多个副版本都要做相 同的事,管理员的工作会变得多么的困难。
活动服务版本应该能通过一个单一的虚拟端点来访问,消费者通过该端点来调用服务。这样做可以减少服务版本管理对其消费者的影响,因为虚拟端点保持不 变,并将请求路由到正确的活动服务版本。兼容性减少了虚拟化的需要,因为同一端点可服务于数代兼容性消费者。将服务进行路由是以它们的主和副版本为基础 的,因而把目标设定为微版本兼容性也会减少服务虚拟端点机制的需要。
服务虚拟化可结合几个 ESB 模式来完成,如抽象端点、智能路由和模式转换。使用服务虚拟化将消费者和提供者隔离了开来,它“动态地”处理消息,在消费者调用或通过事件触发的组合服务的提供者和各版本之间进行路由和仲裁。
虚拟化不仅可用于组合服务,而且从治理的角度看,你在任何分类级别发布的所有服务其实都能从中受益。
互操作性
今天,经济正变得越来越全球化,外包似乎也成为了一种标准做法。公司的业务流程时常会涉及合作伙伴和供应商,组成所谓的“扩展企业”。
扩展企业的组成中会包含异构的系统,正如一家公司的不同业务单位很可能有异构的系统一样(图 8)。在当今的公司中,现役遗留系统的多样性还没有结束。Gartner 最近的一次研究表明,用来交付 SOA 的平台多样性呈增长趋势,就连大机 COBOL 在去年都有了大动作。
在这些扩展企业中,跨异构服务提供者的可组合性会要求互操作性。这会影响你设计服务和模式的方式,包括选择在服务中使用哪种 WS-* 标准。我们的互操作性经验总结如下表。
制品 互操作性 SOAP 遵从 WS-I Basic Profile 1.1 SOAP 1.2 大多数遗留平台和工具要求 SOAP 1.1 WSDL 风格 偏重于“Document/Literal Wrapped” (D/L-W) WSDL 设计 避免使用 wsdl:import 和 xsd:import,因为大多数工具需要一个单独、扁平的 WSDL。Flex 就是这样一个例子。 消息 使用消息交换模式(MEP),如 WSDL 2.0 中定义的 消息设计 对于请求和响应都使用单部分元素(Single part element) 模式风格 偏重于“Venetian Blind”(复杂类型) 模式风格 不要使用匿名类型(“俄罗斯套娃 [Russian Doll]”) 模式设计 支持 XSD 扩展性,在模式通配符之前使用标记元素;总是测试不同平台对模式通配符的支持能力 模式设计 D/L-W 模式组件:只使用元素;禁止属性的使用 模式设计 并非所有平台都支持数据属性的使用或对数据元素的注解 模式数组 使用包装过的数组 / 集合 XSD 类型 并非所有平台都支持所有的 XSD 数据元素(如 xs:date、xs:positiveInteger) XSD nillable 某些 nillable 模式元素可能不被一个平台支持,而其他组件可能要求是 nillable 的 WS* 标准 不同平台间的支持差别很大,总要测试和验证所选标准的互操作性 WS-Security 确保在版本、令牌类型、消息或传输安全、顺序和加密及签名的级别、安全对话的建立(安全会话)上达成一致 WS-Policy, WS-SecurityPolicy 某些平台和工具并不支持这些标准、从而策略元数据不得不借助带外(out-of-band)通信。一个例子就是 Java Spring-WS。表 4. 契约设计指导方针
图 8. SOA 平台不断增长的多样性
本图:“关于 SOA 实施所用开发语言的趋势”出自 Gartner 的报告:“ 2008 年 SOA 用户调查:实施的趋势和特征”,作者为 Daniel Scholler。
总结
在本文中,我们定义了一个以重用为中心的版本管理策略,它支持服务在不破坏服务的现有消费者情况下不断演变以满足新消费者需求。这个方法给服务重用 增加了一个新的维度:在某种程度上,它引入了一个“向前”重用策略,因为“新版本”的服务是由老消费者来重用的,而非相反的方向,即新消费者重用为现有服 务消费者而设计的服务,这是人们传统认为的重用。
从我们的经验看,我们认为,兼容、版本化的数据模型、消息和服务还没有成为 SOA 项目的一个主要关注点。此外,在那些已定义的版本管理策略中,极少 用到了 XML 和 XML 模式扩展性。我们确信,一种基于兼容性的版本管理策略可以增进服务的可交付性、可组合性和真正的重用。它还能减少,尽管无法消除,服 务治理的需要。整体来说,可以预期,这种版本管理策略将极大降低服务的构造、运营和维护成本。超越原始的重用,从你的服务仓库中收获好处的时候到了。
[1] Harmut Wilms, InnoQ, Private Communication
查看英文原文: Contract Versioning, Compatibility and Composability 。
给 InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家加入到 InfoQ 中文站用户讨论组中与我们的编辑和其他读者朋友交流。
评论