【IT168 信息化】
前言
本指南描述了服务总线(Aqualogic Enterprise Service Bus --- 简称:ESB)的一些典型使用场景、设计模式和实践经验。同时对于ESB上一些常见的问题予以解答。
本文的读者应该已经了解ESB的基础概念、进行了相关实践,以及熟悉ESB的管理配置界面。
使用流程元素
ESB提供了丰富的消息流程处理的模型,可以有多种方式来实现。因此需要有一个指导来表明在何时使用哪种方式比较适合。
何处执行转换(Transformations)
路由节点(route node)是消息流树上的一个页节点。如果我们需要转换成或需要转换的消息格式依赖于目标服务,因此在路由节点(route node)或发布节点(publish node)中的请求/响应动作。在发布节点的操作中,任何请求过程中的数据转换是发布动作目标服务所私有的。换一种方式讲,当发布完成后,任何请求信息的改变会回滚到初始状态。另一种请求动作是在outbound上下文变量($outbound)中设定控制变量来影响系统对于发送出去消息的操作(如:设定服务质量QoS等)。
如果转换是在消息请求(或消息响应)过程中执行,而不考虑路由终点,那么需要在请求(或响应)的管道中配置消息转换。
例如,对于一个大批量的订单,需要对收到的SOAP报文生成一个订单汇总,并通过eMail发送给采购经理。为支持这种场景,在请求管道中需要包含一个发布动作。如果订单很大,请求动作需要将该订单转换成一个订单汇总,并将$attachments变量中的附件信息全部删除
假设请求中附件信息的转换不需要理会最终的路由节点,因此建议在请求管道中的一个阶段(stage)将附件进行格式的转换。
假设消息需要根据WS-addressing头信息路由到两个终点中的一个。其中第二个终点(Web Service)需要将SOAP消息体中的订单转换到一个新版本。此时,路由节点需要进行条件路由,以将信息发送到两个终点中的一个,在第二个路由终点的请求动作中需要对订单信息进行格式转换。另外,如果该服务采用的是JMS方式发送,还可以在$outbound变量中设定QoS为“exactly once”。
何时需要在消息流中使用分支
如果一个代理服务提供的WSDL中包含多个操作,通常建议采用操作分支(operational branching)来对每个操作分别处理消息。
如果一个代理服务的类型是 Any SOAP 或 Any XML,就需要在阶段动作中判断消息具体类型,然后需要一个条件分支(conditional branching),通过条件分支节点来根据消息类型路由消息 。
条件分支可以用于代理服务的最外层,来对外提供路由选择。例如:如果需要根据条件来调用服务A或服务B,上述动作如果不在路由节点中实现的话,就需要在消息流中配置条件路由。在每个分支中使用路由节点作为其子节点,这是一种典型的决策方式,但如果消息分支数量比较多的时候,配置每个分支中子节点的路由就不能算是一个好的方案了。
设计单一或多阶段(Stage Action)
管道中单一的阶段通常能够满足大多数使用案例。但是有些情况下,需要考虑多个阶段动作(stage action)。
相比于把所有用户操作放置到一个阶段动作中,采用多阶段的设计模式提供了一个自然的模块化设计机制。
在请求或响应的管道中,每个阶段可以具有独立的差错处理能力。当在管道中采用多个阶段操作,就可以避免在一个差错处理中来考虑各种动作所产生的差错处理。
如果使用了resume 动作(在差错处理中)或 skip 动作,其行为是在请求/响应管道的下一个阶段恢复处理。因此考虑到需要强调的业务逻辑,将resume 动作或 skip 动作之后的动作放置到下一个阶段中是一个好的设计原则。
在一个阶段中典型的动作包括:
消息的转换
消息汇报(report action)
通过一个循环,来进行WSCallout 以进行消息的丰富或路由
校验消息是否合法和抛出错误提示
发布消息的副本到一个终点目标
何处处理差错
ESB提供了三种差错处理的选择
在请求/响应管道中通过一个测试来校验。如果断言是真,就通过reply动作(或reply with failure)处理
在每个阶段层面、路由节点、管道节点或服务层等来捕获和处理消息
通过系统自带的差错处理机制来处理错误
通常情况下,最容易的差错处理是在最低的级别进行捕获,使用高级别的差错处理来处理更普遍的差错。但是对于inbound 的WS-Security 相关的差错只能在服务层进行捕获和处理。
如果输入的消息是一种请求/响应型的消息,差错处理还需要包括产生应答消息通过reply with failure动作发送回去。在HTTP方式,reply with failure会产生一个HTTP 500状态信息。而在JMS方式,reply with failure会将JMS属性JMS_BEA_Error设置成为ture。
如果服务是通过另外一个代理服务而调用的,其返回SOAP错误或传输错误,差错处理管道会被调用。对于SOAP访问,如果reply with failure动作被执行,则系统会将最初的SOAP错误会返回给调用者。否则,系统错误处理机制会产生一个新的SOAP错误信息发送回去。代理服务可以识别SOAP错误,但它通常是不需要检测消息(此种设计主要是考虑除非需要,可以尽量避免解析消息内容),因为HTTP返回状态设定的不是200和 202,或者JMS属性JMS_BEA_Error设定为true。而对于非SOAP的消息,JMS_BEA_Error = true也会被设定以标识这是一个差错响应。
通常情况下,明确的对可预期的差错在管道之中进行处理是一个好的习惯,采用系统的差错处理机制只可用于不可预期的差错情况。
某些用例调用会报告一个错误,使用报告(report)动作可以处理此场景。例如:当请求管道报告消息以便跟踪时,但在报告动作后,其路由节点中的服务调用失败,报告消息只能解析为消息已经被提交处理。差错处理中的报告处理失败只能进行补偿操作,当有人在管理控制台中使用报告系统可以跟踪该消息,判断出最初提交的消息和其后续的差错表明该消息没有被正常处理。
判断使用何种类型服务
服务总线支持各种类型的服务,包括Web Services(使用XML或WSDL中的SOAP绑定),以及各种非XML或其他通用的服务。
如果一个服务具备一个定义良好的WSDL接口,使用WSDL来定义服务。它具有以下可选的优势:
系统提供了对WSDL中每个操作的衡量
可以在管道中使用操作分支(operational branching )
当服务是被代理服务调用,SOAP动作的报文头会自动发布
在XQuery 和 XPath 编辑器和条件构建器中,可以十分方便的操作$body变量,因为编辑器会自动将请求消息的$body消息映射到WSDL上。但是,实际运行状态中$ body信息内容可能对特定动作中会与缺省的内容有所不同。这是因为服务总线是一种非传统的编程语言,它不会生命有类型的变量并使用。相反其中的变量都是无类型的,并在运行状态时动态的创建和使用。另外变量的类型会在创建时来表明变量所包含的内容。设计时编辑器允许在一个指定动作中映射变量,依此简化 XQuery的编写。服务总线本身并不知道变量内容的类型。
如果服务使用 WS-Security, WSDL 是必须的 (WS-Policies同时需要附在该WSDL上).
系统支持 <url>?这种WSDL语法,这可以动态的获得一个HTTP代理的WSDL,这对一些SOAP客户端代码生成工具是非常有用(如:BEA Workshop)。
服务总线并不自动根据接口定义(WSDL或消息接口定义)来校验服务发送或接收的消息。但是通过将服务接口定义放置到某一位置,在将来是可以通过打开开关方式来让系统对消息进行校验,虽然上述校验的代价比较大。目前对于消息流的设计者使用校验动作和XQuery条件表达式来在消息流中进行明确的校验。
服务总线不自动做必须理解的SOAP消息头的合法性校验,而是可以在消息流中通过Xquery明确的进行处理。另外将来服务总线是可以增加选项来进行该项工作
当使用WSDL,在下述场景中,建议选择WSDL Port方式来绑定服务而非直接绑定:
通过?WSDL语法产生的WSDL中,port名称被保留,这对于一些客户端工具非常重要,但是服务的URL会被服务定义中transport部分的URL定义所覆盖。绑定服务的WSDL中的URL不会被使用,除非它是在业务服务定义中缺省的URL定义
任何 WS-Security 策略在port层面被使用的状况下
如果用户希望暴露一个port给客户端,以涵盖后端各种企业应用,可以使用Any SOAP或Any XML服务类型。
如果消息(请求/响应)中至少一个类型不是XML,则使用 messaging service 类型定义服务。
对于各种类型的消息,如何理解上下文
对于整个流程,存在一个消息,该消息存在于$header, $body和$attachments变量中。即使服务类型不是SOAP方式,消息的规范模式遵循SOAP格式,在上下文中,消息的体现方式为SOAP格式。
$header 变量包含SOAP Header 元素, $body 元素包含SOAP Body 元素,而$attachments变量则封装了附件,每个附件为一个子附件元素。附件元素包含body元素来表示真实的附件数据。
如果消息格式是二进制格式,$body变量中的Body元素包含一个XML的指针元素,它表示二进制数据内容的指针。而在二进制附件数据,附件元素中的body元素同样包含数据的指针信息。
对于MFL格式的消息,$body 变量中的Body 元素则按照MFL文件所定义的XML格式体现。
对于文本数据,$body 变量中的Body 元素为文本。而对于文本格式的附件,$attachments变量中body元素为文本。
对于XML格式的附件,$attachments变量中body元素为XML。
下面给出了这些元素的格式定义,这些元素的命名空间为预先定义的”ctx” :
<element name="attachments" type="mc:AttachmentsType"/>
<!-- Each attachment in the 'attachments' variable is represented by an instance of this element
<element name="attachment" type="mc:AttachmentType"/>
<!-- Element used to represent binary payloads and pass-by reference content
<element name="binary-content" type="mc:BinaryContentType"/>
<complexType name="AttachmentsType">
<sequence>
<!-- the 'attachments' variable is just a series of attachment elements -->
<element ref="mc:attachment"minOccurs="0"maxOccurs="unbounded"/>
</sequence>
</complexType>
<complexType name="AttachmentType">
<sequence>
<!-- Set of MIME headers associated with attachment -->
<element name="Content-ID" type="string" minOccurs="0"/>
<element name="Content-Type" type="string" minOccurs="0"/>
<element name="Content-Transfer-Encoding" type="string" minOccurs="0"/>
<element name="Content-Description" type="string" minOccurs="0"/>
<element name="Content-Location" type="string" minOccurs="0"/>
<element name="Content-Disposition" type="string" minOccurs="0"/>
<!-- Contains the attachment content itself, either in-lined or as <binary-content/> -->
<element name="body" type="anyType"/>
</sequence>
</complexType>
<complexType name="BinaryContentType">
<!-- URI reference to the binary or pass-by-reference payload -->
<attribute name="ref" type="anyURI" use="required"/></complexType>