内容


WS-RM 和 WS-R:SOAP 是否可以从混淆中可靠传递?

两个可靠的传递规范造成了混淆

Comments

这两个规范试图解决什么问题?

网络有时候会出现故障,这是 Web 用户必须忍受的,无法更改的事实。不得不点击浏览器上的 Reload 按钮是您必须忍受的烦恼之事,因为您会返回一些不明确的 Internal Server 或者 Server not found 错误。并且对于大多数情况,点击 F5 也是可以的 -- 可能不在 Buy It 页面上,因为您并不知道是否该系统会将您的信用卡刷两次,但是大多数情况下它都工作得非常好。然而,当计算机之间互相对话的时候,它们要执行非常特殊的任务和详细的数据块以实现共享。这些消息丢失任何一个都可能导致严重的后果。例如银行,已经花费了大量的金钱(和时间)来确保他们不同的计算机系统之间的事务/消息从来不会丢失。换句话说,他们想要确保他们的数据可靠地传输到恰当的目的地。因为 Web 服务/简单对象访问协议 (SOAP) 延续了它在整个行业中的扩展使用,以互操作的方式来保证传输的需求对它的成功非常重要。

解决这个问题的一种方法是向服务器发送一个消息并且等候响应。如果在一定的时间以后您并没有收到服务器的响应,再重新发送消息。这种方法中仍然存在问题。首先,如果该消息发送是没有遵照请求-响应消息模式(例如,它们是单向 (one-way) 消息),接收返回的 HTTP 200 或者 202 响应代码并不一定保证服务器收到了消息。在定义中,202 意味着该消息仅仅在 HTTP 层上是 Accepted,但是并不一定被 SOAP 处理器处理。同样,客户端堆栈可能会通过不同的方式来解释单向消息。例如,它可能仅仅打开了连接并且发送了消息,并没有关心发送的成功或者失败(启动并忽略)。

然后,当然,也有一些情况下,客户端注意到错误的相应代码;您不能简单的重试。获取一个并非 HTTP 200 的响应并不能保证服务器没有成功的传递消息。例如,可能服务器获取了该消息并且正确地进行了处理,但是在试图处理响应消息的时候发生了一些错误。由于收到 HTTP 500 响应代码而重新发送消息,服务器将对该消息处理两次,这样可能会产生非常严重的负面影响。

另一种方法需要所有的应用程序消息都具有某种响应消息。这样即使响应消息为空,也将使您能够收到一个消息的确认或者收到信息。请注意这种方法有助于您获得传递的确认(在成功的情况下),但是它并不能帮助解决同样消息发送两次的问题(正如我在上一段中讨论的那样。为了解决这个问题,应用程序必须具备一些逻辑来避免重复消息)。不是不可能但是非常麻烦。在理想情况下,任何解决方案的开发应该在比应用程序低的级别上发生,不去管它而集中于实际的工作中。为保证消息的传递和以前的传递,需要客户端和服务器端进行一些握手。因为 SOAP 的主要的目标之一是在各种 SOAP 堆栈之间的互操作性,这个握手需要行业标准,并且因此,这意味着还需要另一个 WS-* 规范。Web 服务社区有两个规范来解决这个问题:WS-ReliableMessaging (WS-RM)WS-Reliability (WS-R)。在我讨论这两个完整的规范的不同之前,让我们先讨论一下它们的相似之处,因为他们基本上都是解决同样的问题,在相同的概念上确保 SOAP 消息可靠传递。

可靠传递

这两个规范都允许 SOAP 堆栈将这个问题推到一个较低的级别上,特别是到中间件层。一旦应用程序通过 SOAP 堆栈来处理它的消息,基本上它可以忘记它(它将担保 SOAP 堆栈将消息传递到恰当的目的地)可以说。有时候您可能遇到 SOAP 堆栈不能到达消息的目的端点。在这种情况下,客户端 SOAP 堆栈需要向发送应用程序通报失败( 但是这些是不能恢复的错误。例如,目的地 URL 中一个印刷错误。SOAP 堆栈并没有方法从中恢复。这些可靠传递规范的设计允许在临时网络失灵的情况下消息仍然到达最终的目的地。这两个规范都讨论了保证消息的成功传递或者故障会产生的结果。)这些是两个唯一可能的结果。

这两个规范后面基础的思想都是消息的客户端(或者发送方)连续的重新发送消息,直到它收到证实(或者确认),服务器(或者目的地)已经成功地获得了消息。与前面讨论的一个可能的解决方法不同。然而,这也同样假设在两个端点之间一些共同的理解。首先是 Acknowledgment 或者 ACK 的定义;两个 SOAP 堆栈都必须理解 ACK 是什么:其像什么,它意味着什么,以及如何发送它。第二,双方都必须在如何发送消息的语法上达成一致。正如前面提及的那样,发送端必须同意持续重新发送消息直到它收到该消息的 ACK。同样,接收方不仅要同意一旦收到消息,立刻返回一个 ACK,还必须忽略任何重复的消息。在其核心处,几乎包含了全部两个规范 -- 让我们讨论一个实例来整体论证一下它是如何工作的。例如,我将使用 WS-RM 中的 XML 片断。稍后我将讨论 WS-R 的不同点。

图 1 演示了一个简单的图表,强化说明了下面要讨论的消息流。请注意发送方怎么将三个消息作为一个单独 sequence 的一部分来发送。两个规范都有意识到需要将一系列消息组合在一起,因此您不仅能保证它们的传递,还能保证它们传递的顺序(如果在处理 Message 1 之前处理 Message 2 可能导致严重的后果)。在 图 1 中,您看到在三个消息的首次发送中仅 Messages 1 和 3 成功了。

图 1. 基本的可靠传递消息流
fig1
fig1

在首批消息以后,请注意目的地怎么发送回 ACK 来标志它收到了 Messages 1 和 3,这是发送方意识到它仅仅需要重新发送 Message 2。并且最后,一收到 Message 2,目的地就可以发送回最终的 ACK 来标志它收到了这个队列中的所有三个消息。需要额外注意的是,Message 3 被标记为队列的最后一个消息。这个信息有多种用处。至少,目的地可以用它来了解序列中的最大消息数量,因此如果它收到任何比 3 大的消息号时可能会返回错误。

WS-RM 的一个例子

现在让我们看一下使用 WS-RM 的话消息是什么样子。首先,让我们从 Message 1 开始,如下面的清单 1 所示。

清单 1. 样本 WS-RM 消息
  <soapenv:Envelope>
   <soapenv:Header>
    <wsa:MessageID> ... </wsa:MessageID>
    <wsa:To> ... </wsa:To>
    <wsa:Action> ... </wsa:Action>
    <wsa:From> ... </wsa:From>
    <wsrm:Sequence soapenv:mustUnderstand="1">
     <wsu:Identifier>
      http://www.ibm.com/guid/a8f7151a091b50a42b38e04437774e11
     </wsu:Identifier>
     <wsrm:MessageNumber>1</wsrm:MessageNumber>
    </wsrm:Sequence>
   </soapenv:Header>
   <soapenv:Body> ... </soapenv:Body>
  </soapenv:Envelope>

<wsrm:Sequence> 消息头是额外的特定于 WS-RM 的数据,作为消息的一部分。该消息头内部是序列的唯一标识符,允许目的地了解这个消息属于哪个(可能多个)序列。数据的其它部分是 MessageNumber 元素,指明这是 Message 1。但愿这看起来相当的直接,该规范的作者(两个规范)在使他们的解决方案尽量简单这一方面做得非常好。

清单 2 中,Message 2 的 XML 看起来几乎一样,除了在 MessageNumber 中用“2”来替换“1”。Message 3 几乎做了相同的修改:

清单 2. 序列中最后的 WS-RM 消息
  <soapenv:Envelope>
   <soapenv:Header>
    <wsa:MessageID> ... </wsa:MessageID>
    <wsa:To> ... </wsa:To>
    <wsa:Action> ... </wsa:Action>
    <wsa:From> ... </wsa:From>
    <wsrm:Sequence soapenv:mustUnderstand="1">
     <wsu:Identifier>
      http://www.ibm.com/guid/a8f7151a091b50a42b38e04437774e11
     </wsu:Identifier>
     <wsrm:MessageNumber>3</wsrm:MessageNumber>
     <wsrm:LastMessage/>
    </wsrm:Sequence>
   </soapenv:Header>
   <soapenv:Body> ... </soapenv:Body>
  </soapenv:Envelope>

在这个消息中,您会看到 WS-RM 序列消息头还包括一个 LastMessage 元素,表明是这个序列中的最后一个消息。看,没什么太复杂的。现在让我们看一下目的地的 ACK 是什么样子:

清单 3. WS-RM SequenceAck
  <soapenv:Envelope>
   <soapenv:Header>
    <wsa:MessageID> ... </wsa:MessageID>
    <wsa:To> ... </wsa:To>
    <wsa:Action> ... </wsa:Action>
    <wsa:From> ... </wsa:From>
    <wsrm:SequenceAcknowledgement>
     <wsuu:Identifier>
      http://www.ibm.com/guid/a8f7151a091b50a42b38e04437774e11
     </wsuu:Identifier>
     <wsrm:AcknowledgementRange Lower="1" Upper="1"/>
     <wsrm:AcknowledgementRange Lower="3" Upper="3"/>
    </wsrm:SequenceAcknowledgement>
   </soapenv:Header>
   <soapenv:Body> ... </soapenv:Body>  (response body)
  </soapenv:Envelope>

当目的地阐明其响应时,它在消息中包含一个附加的 SOAP 消息头 (ACK)。如上面的清单 3 所示,您可以看到它包含对于该序列和两个 AcknowledgementRange 元素的标识符。您可以注意到目的地仅仅收到了 Messages 1 和 3。在请求-响应消息模式的情况下,可以在响应消息上 piggy-back 这个 ACK。对于单向消息,目的地 SOAP 堆栈需要生成一个新的 SOAP 信封(包含一个空的 soapenv:Body)来添加这个 ACK 标签。事实上,这极大地简化了如何向发送方返回 ACK,因为两个规范都能将 ACK 发送到同发送方完全不同的端点。但是现在,已经知道 ACK 被发送到合适的地方就可以了。

因此完成这一场景,一收到这个 ACK,发送方就重新发送 Message 2 且最终获得一个 ACK,如下面的清单 4所示:

清单 4. 最终的 WS-RM SequenceAck
  <soapenv:Envelope>
   <soapenv:Header>
    <wsa:MessageID> ... </wsa:MessageID>
    <wsa:To> ... </wsa:To>
    <wsa:Action> ... </wsa:Action>
    <wsa:From> ... </wsa:From>
    <wsrm:SequenceAcknowledgement>
     <wsuu:Identifier>
      http://www.ibm.com/guid/a8f7151a091b50a42b38e04437774e11
     </wsuu:Identifier>
     <wsrm:AcknowledgementRange Lower="1" Upper="3"/>
    </wsrm:SequenceAcknowledgement>
   </soapenv:Header>
   <soapenv:Body> ... </soapenv:Body>
  </soapenv:Envelope>

ACK 表明所有的消息都已经收到了。如前面所述,如果额外的逻辑(重试)在中间件层结束,连接双方的 SOAP 堆栈可以管理消息的可靠传递(或者排序)而不用对应用程序做任何修改。

因此,不同点是什么?

该 XML 片断是 WS-RM 的一个使用。WS-R 遵从同样的基础设计,但是它的 XML 看起来稍微有些不同。然而,两种规范之间确实有值得我们注意的差别:

WS-RM

该规范:

  • 允许在应用程序消息之前传递一个额外的 createSequence 消息,在应用程序消息之后传递一个额外的 terminateSequence 消息。应用程序的这个 bookending 允许目的地明确的定义序列标签使用的序列标记符。并且通过客户端发送一个最后的 terminateSequence 消息,目的地了解到所有的 ACK 都已经被接收,并且现在可以忘记整个序列。通过使用 createSequence 操作,目的地可以拒绝任何没有预先知识(安全功能)的序列。
  • 使用 WS-Addressing 规范。
  • 具有 ACK 的概念。这允许目的地向发送方发送通知,告知哪个特定的消息需要重新发送。这允许使用一个更加优化的重发机制。
  • 还没有提交给标准组织(例如 World Wide Web Consortium (W3C)或者 Organization for the Advancement of Structured Information Standards (OASIS))。

WS-R

该规范:

  • 允许发送方包括传送保证规则以作为消息的一部分。例如,它可以包含一些信息,表明该消息应该“按顺序”传送且不能重复。WS-RM 也有这些传送保证规则,但是并不是消息的一部分。它们通过其他的方法来实现,例如 WS-Policy
  • 允许发送方 poll ACK 的目的地。这使处在防火墙后面的发送方仍然可以获得 ACK。WS-RM 没有定义任何方法用来使发送方异步接收 ACK。它们必须在 HTTP 响应流中或者防火墙外的端点中返回。
  • 不依赖 WS-Addressing。
  • 是 OASIS 标准。

虽然不同点的列表很短,它确实说明了两个规范之间的一些类似之处。事实上,他们都提供了可比较的特性。因此,为什么有两个?您因该使用哪一个呢?

从这里您要到哪里?

对于第一个问题的一个简短的回答是确定两个不同的组来并行且独立地攻击问题。两个规范的结果如此的类似,这一事实表明两个组的创始人都通过了相同的足迹。

使用哪一个?这个比较困难,原因很明显。不管我是否属于 IBM(IBM 是 WS-RM 的创始人之一),我都很喜欢 WS-RM。它同其它行业认同的 WS-* 标准(例如 WS-Addressing)在大方向上更加一致。这就是说,我确实相信(或者可能仅仅是一个期望)两个组将结合他们的努力并产生一个单独的规范。不仅仅因为利用两个规范的优点会产生一个更好的解决方案,还因为 Web 服务组织不会被强制做这个选择。

可能这是不可避免的,因为您要提升处理堆栈,公司的不同组织将希望通过他们自己的方式来解决这类问题 -- 导致像这些这样的更多选择(考虑 WS-RF verses WS-Transfer 作为另一个实例)。但是 Web 服务社区必须记住 SOAP 的原始出发点:互操作性。如果他们允许复制结果、设计以及规范以变成通用的地方,它们将失去 SOAP 的最大优点:为新出现的事物打开大门。

如果您对使用 WS-RM 感兴趣,看一下 Emerging Technologies Toolkit。该工具包包含一个演示以及一些示例,使您可以体验该项技术。在 Web 站点也有一个例子,使用 WS-RM 互操作车间场景作为它的基础。如果您想要查看运行中的 WS-RM 的话请使用这个,不需要下载和安装任何东西。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=SOA and web services
ArticleID=81452
ArticleTitle=WS-RM 和 WS-R:SOAP 是否可以从混淆中可靠传递?
publish-date=03012004