级别: 中级 Shawn X. K. Hu (shu@xtensible.net), 解决方案架构师, Xtensible Solutions
2006 年 11 月 15 日 使用 Web 服务时,达成一致的 XML 模式能否保证在系统间进行成功的数据交换? 您可以在本案例研究中找到答案。您将了解如何在涉及集成层时定义 WSDL 文档来在 SOAP 消息级别实现互操作性。文中对 Web 服务的远程过程调用(Remote Procedure Call,RPC)和 Document 样式进行了讨论。
引言
在基于 Web 服务的 SOA 设计中,通常需要为提供者和使用者定义 XML 模式来进行数据交换。定义这种共同达成一致的模式是互操作性的第一个步骤,但并不一定保证进行成功的数据交换。为了通过使用 Web 服务确保数据事务总体可互操作,还必须考虑 SOAP 消息。
XML 有效负载通常包装在 SOAP 消息中,以便进行数据传输。如果没有正确的 SOAP 消息设计,则可能无法正确交换 XML 有效负载,即使有效负载遵循提供者和使用者的公共 XML 模式也是如此。SOAP 消息结构由 WSDL 文档中的 SOAP 绑定定义确定。本文中的案例研究说明了 WSDL 定义会如何影响 SOAP 消息和数据交换。
案例研究
以下序列图说明了一个案例,其中涉及到使用集成层来在基于 Web 服务的 SOA 中进行数据交换。System A 是使用 Web 服务将其数据发布到集成层的数据系统,而集成层使用 Web 服务将数据中继转发到下游系统,如 System B。集成层充当数据事务的中间层服务。它可提供服务聚合和协调等功能。在此案例中,集成层控制数据系统 A 和 B 之间的数据流。
从理论上讲,基于 SOA 的集成中并不一定需要集成层,但事实上,如果没有这个层,成功的可能性就很小。我们此处给出的案例的情况适用于使用集成层进行数据流控制和服务协调的 SOA,可提供要集成的系统的必要分离。本文重点讨论如何恰当设计 Web 服务,以在 SOAP 级别实现数据交换互操作性。
此数据交换案例中涉及到两个 Web 服务:publishSomeData 和 receiveSomeData 服务。publishSomeData Web 服务由集成层实现,用于发布来自 System A 的数据。System A 是 publishSomeData Web 服务的使用者。它将调用集成层的“publish”服务,以发送 publishSomeDataRequest 消息,将其数据以包装在 SOAP 信封中的 XML 格式进行广播。集成层接收到此消息后,会立即向 System A 发送一条响应消息 publishSomeDataResponse。
集成层接收到消息有效负载后,将与其协调服务进行联系,确定哪些系统订阅了此数据。它将随后通过调用 System B 提供的“receive”服务 receiveSomeData 把消息发送到接收方 System B。
为了简化案例研究,我们使用以下 XML 消息有效负载作为示例:
清单 1. XML 消息有效负载
<SomeData>
<ID>123456789</ID>
<Value>33.99</Value>
</SomeData>
|
这段 XML 有效负载可以采用两种不同的样式在 SOAP 消息中进行表示:远程过程调用 (RPC) 和 Document。在 RPC 样式中,XML 有效负载包装在 SOAP 主体中的 operation 元素内。另一方面,Document 样式的消息将 XML 有效负载直接放置在 SOAP 主体中。RPC 和 Document 消息都可以为 Literal 或 Encoded 消息。由于 Encoded 样式并不受 WS-I 支持,因此我们将不在本文中对其进行讨论。Literal 消息表明要使用模式来提供对 SOAP 中的 XML 有效负载的描述和约束。在此案例研究中同时使用了 RPC-literal 和 Document-literal 样式。
SOAP 绑定,RPC 样式
在 RPC-literal 样式中,服务使用者会将 Web 服务作为远程过程调用进行调用。它将发送带有作为参数的输入的消息。在通过 HTTP 等传输协议进行调用前,Literal XML 有效负载被封装在 SOAP 消息中。
从服务使用者 System A 发出的典型 RPC SOAP 消息如下所示:
清单 2. 从服务使用者 System A 发出的 RPC SOAP 消息
<soap:Envelope >
...
<soap:Body>
<publishSomeData>
<SomeData>
<ID>123456789</ID>
<Value>33.99</Value>
</SomeData>
</publishSomeData>
</soap:Body>
</soap:Envelope>
|
“publishSomeData”是 System A 调用的操作的名称。“SomeData”内容是操作的输入参数。
正如上面所提到的,SOAP 消息的构造方式主要在 WSDL 文档中确定。要构造此类 SOAP 消息,可以在 WSDL 文档中将 SOAP 绑定定义为:
清单 3. 在 WSDL 文档中定义 SOAP 绑定
...
<wsdl:binding name="PublishSomeData_Binding" type="tns:PublishSomeData">
<soap:binding style="rpc"
transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="publishSomeData">
<soap:operation style="rpc" soapAction=
"http://www.someCompany.com/soa/2005-03-06/publishSomeData"/>
<wsdl:input>
<soap:body use="literal" namespace=
"http://www.someCompany.com/soa/2005-03-06/publishSomeData.wsdl"/>
"http://www.someCompany.com/soa/2005-03-06/publishSomeData.wsdl"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal" namespace=
"http://www.someCompany.com/soa/2005-03-06/publishSomeData.wsdl"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
|
请注意,soap:binding style 为“rpc”,而 wsdl:operation name 是 SAOP 消息中的“soap:Body”内包含的元素。
数据流从 System A 调用集成层的 publishSomeData 服务开始。集成层接收到 SOAP 信封中的 XML 消息后,会使用确认信息响应 System A。集成层随后通过调用接收方端的 receiveSomeData 服务将 SOAP 消息传递给接收方。通常,集成层并不会打开 SOAP 信封来处理 XML 有效负载。同样,它仅充当控制数据流的中间层服务,并确保正确地接收数据。
在接收方端,System B 根据其自身的 WSDL 定义预期接收采用 RPC 样式的传入的 SOAP 消息。作为“receive”服务,其 WSDL 操作通常按照以下方式进行定义:
清单 4. WSDL 操作
<wsdl:operation name="receiveSomeData">
<soap:operation style="rpc" soapAction=
"http://www.someCompany.com/soa/2005-03-06/receiveSomeData"/>
<wsdl:input>
<soap:body use="literal" namespace=
"http://www.someCompany.com/soa/2005-03-06/receiveSomeData.wsdl"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal" namespace=
"http://www.someCompany.com/soa/2005-03-06/receiveSomeData.wsdl"/>
</wsdl:output>
</wsdl:operation>
|
请注意,“receive”服务中的操作名为“receiveSomeData”,与集成层为“publish”服务定义的操作并不一样。操作名通常基于其在 SOA 中的角色定义,如“publish”用于发布服务,而“receive”用于接收服务。在接收方端,服务的角色是“receive”。
根据 wsdl:operation 的定义,接收方 Web 服务接口将预期接收通过 receive 操作包装的 RPC 样式的 SOAP 消息,如下所示:
>清单 5. 通过 receive 操作包装的 RPC 样式的 SOAP 消息
<soap:Envelope >
...
<soap:Body>
<receiveSomeData>
<SomeData>
<ID>123456789</ID>
<Value>33.99</Value>
</SomeData>
</receiveSomeData>
</soap:Body>
</soap:Envelope>
|
显然,无法对集成层从源系统 System A 中继转发的 SOAP 消息进行处理,因为其中包含一个不同的操作名称“publishSomeData”。因此,在这种情况下进行的数据交换将失败。
可以采取很多方法解决这个问题。其中一种方法是,让集成层处理 SOAP 消息,然后使用恰当的操作名称重新构造另一个 SOAP 消息。不过,这样会导致在集成层进行额外的处理工作。解决此问题的一个首选方法是,使用 Document-literal 样式的 SOAP 消息,并假定对于这个特殊集成,“rpc”和“Document”样式都是允许的。
SOAP 绑定,Document 样式
在 Document-literal 样式的 SOAP 消息中,SOAP 主体仅包含 XML 有效负载,而不会包装操作名称。典型的 Document 样式 SOAP 消息与以下所示内容类似:
清单 6. 典型的 Document 样式 SOAP 消息
<soap:Envelope>
...
<soap:Body>
<SomeData>
<ID>123456789</ID>
<Value>33.99</Value>
</SomeData>
</soap:Body>
</soap:Envelope>
|
请注意,操作名“publishSomeData”没有再出现在 SOAP 主体中。SOAP 主体仅包含 XML 有效负载。为了构造上面列出的 SOAP 消息,其相应的 WSDL 绑定定义如下:
清单 7. WSDL 绑定定义
...
<wsdl:binding name="PublishSomeData_Binding" type="tns:PublishSomeData">
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="publishSomeData">
<soap:operation style="document"
soapAction=
"http://www.someCompany.com/soa/2005-03-06/publishSomeData"/>
<wsdl:input name="SomeData">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="returnData">
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
|
现在让我们再次看看从 System A 通过集成层到 System B 的数据流。集成层的操作并没有什么不同。它接收来自 System A 的数据,并随后直接将其传递给接收方 System B。接收方端的 Web 服务必须定义为接收 Document 样式的消息,以便在这种情况下进行数据交换。这样,就可以避免 RPC 样式的互操作性问题,因为 SOAP 消息中并不会出现任何特定的操作名称。
Document-wrapped 样式
对于 Document 消息,还有两种其他样式:Wrapped 和 Unwrapped。Wrapped Document-literal 样式用于模拟 RPC 样式。在 Document wrapped 样式中,wsdl:operation name 必须定义为与 XML 文档的根元素的名称相同。上面列出的 Document 消息是采用不同操作和模式元素名称的 Unwrapped Document 样式。在下面的 Document-wrapped SOAP 消息中,模式根元素被重命名为“publishSomeData”,以与在其 WSDL 文档中定义的操作名称匹配。
清单 8. Document-wrapped SOAP 消息
<soap:Envelope >
...
<soap:Body>
<publishSomeData>
<ID>123456789</ID>
<Value>33.99</Value>
</publishSomeData>
</soap:Body>
</soap:Envelope>
|
现在,XML 在根元素中作为“publishSomeData”出现,而此名称与“publish”服务的 wsdl:operation 名称相同。
只要在接收方的 WSDL 中定义了相同的名称,接收方端就不应有与接受此类 Document 样式 SOAP 消息相关的问题。不过,这违背了应在接收方端定义“receive”操作而在发布端定义“publish”操作的 SOA 角色模式。在“receive”模式中使用“publish”元素完全违反了“receive”服务的语义。在这种情况下,可以将相应的语法更正为 Document-wrapped 样式的,但这样并不一定是 SOA 实现的好方法。
结束语
总之,必须在 SOAP 级别和 XML 有效负载级别实现互操作性。各种 SOAP 消息样式都可以通过在 WSDL 进行不同的组合来用于进行数据交换。某种样式并不一定要比其他样式好,但任何消息样式选择都应该满足业务和集成需求。在本文提供的案例中,Document-literal 和 Unwrapped 的样式组合比较合适。
参考资料 学习
获得产品和技术
讨论
关于作者  | |  | Shawn Hu 是 Xtensible Solutions 的一名解决方案架构师。他的专长包括 Web 服务设计 (WSDL)、数据分析和建模以及 XML/XSD 生成。他曾参与过各种系统集成项目,工作重点是接口设计和数据流。他获得了加拿大西安大略大学的博士学位。 |
对本文的评价
|