代码改变世界

WCF SOAP消息剖析

2011-07-22 16:06  田志良  阅读(3748)  评论(3编辑  收藏  举报
  小时候,我们学习到邮票应该贴在信封的右上角,地址应该写在中间。如果我们愿意,可以增加一个回复地址在信封的左上角。所有被处理的信件必须遵守这个基本的结构。如果格式不对,或者地址不清晰,或者地址不合法,邮政服务会认为这个邮件无效,并且无法投递。如果我们幸运的话,邮件会被退回(如果写地址的话)。可以想象没写地址有多混乱。如果发送者可以允许乱放邮票或者地址,邮政服务需要查遍整个信封来确定邮票和地址。很可能,为了完成新加投递任务,每次可能要增加远多于2美分的邮资。实际上,邮局定义的信封结构,从发信人角度来看,会改进信件处理的效率和一致性而不需要牺牲可用性。
  与邮寄信件的例子相反,SO消息不需要在遵守结构模式。像邮寄信件的例子,一个定义好的信封格式改进提高工作效率,可靠性和系统的功能。记住消息应用系统概念不是新的。起源于各种各样不同厂商的消息已经经历过了几十年来应用的发展。没有标准的结构,各个厂商偶读可以开发自己的消息架构,结果就是这些千差万别的消息架构不能够用来进行系统间的互操作。
  如果我们看一下FedEx, UPS, 和DHL这些公司,我们可以看到一个相似的模式。每个组织已经定义好的他们自己的地址格式和打包规范。很难看到一个贴着UPS标签的过夜包裹在FedEx公司里投递。技术角度来说是可行的,但是商业压力和效率阻碍了这些公司于另外一个公司的地址格式和包装标准通用。
  用相同的概念检验一个可收购企业的计算系统并不苦难。总体来说,厂商不希望自己的应用系统可以与其他的系统交互。他们有足够的时间让自己的系统与单个设备通信,单独与其它系统互操作。过去,客户希望,在一定程度上,用一个厂商的工具集解决他们所有的企业需求。客户面对的选择是“谁能卖给我这样的完整包裹?”而不是“那个产品最适合我的每个需求?”。随着社会发展,一站式销售很难满足潜在的客户。结果,软件厂商不得不坐到一起来开会,来制定一些列公共消息规范和标准,让他们的应用系统产生的消息遵守这些规范。为了这些标准的简历和通过,已经花费的很多年时间,但是最终这些标准诞生,并且我们可以希望这些标准日益完善。
  有许多关于消息标准的文章,在阅读本书的时候我们可以多次提到这些标准。这些规范是,不论哪种形式,许多都是基于SOAP,并且每个规范的作用不同。出于学习上的好奇心,完全的SOAP消息规范可以在这里查看:http://www.w3.org/TR/soap12-part1/。由于SOAP的灵活性,现在SO消息的消息都是SOAP消息。SOAP,它的核心,是一个基于XML的消息结构。SOAP定义了3个主要的可以定义任意XML消息的XML元素:信封,消息主体和消息头。这里是个简单的SOAP消息的例子:
<?xml version='1.0' ?> 
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"> 
<env:Header> 
</env:Header> 
<env:Body> 
</env:Body> 
</env:Envelope>

消息信封

        正如名字的含义一样,消息信封包装消息体和消息头。所有的SOAP消息都有一个消息信封作为根元素。消息信封经常被用来定义不同的贯穿消息的命名空间(和前缀)。这令人对SOAP消息非常兴奋。

消息头

        消息头是可以选择的,如果出现的话,必须是消息信封envelope标签的下第一个元素。一个SOAP消息头由一个或多个SOAP消息头块组成。SOAP消息头块包含可以被最终接受者和中介者使用的信息。典型的就是,这些消息头块包含描述消息体数据的信息。换句话说,安全信息,关联或者消息上下文都可以放到消息头部分里。如果需要特定的消息行为,消息头块是必须的。再补充一次,这个想法可以由邮政系统来阐明。如果我想通过邮政系统发送一个信件并且希望在信件到达以后得到一个反馈,我必须填写收信信息并且把它贴到信封上。填写退回地址不会改变信件的内容。它可以改变消息参与者的行为:我必须填写和黏贴退信的信息。邮递员必须所要签名,最终的接受者必须签字接收,邮递员必须返回签收邮件的收据给我(至少是我的邮箱)。

        SO消息可以包含相似的信息在消息头里。比如,在我们的订单处理场景里,与路由的确认信息相比,网站或许更想接受消息接受实体的确认信息。这个例子里,网站可以为消息指定一个标识,然后加到请求确认的消息头里。在到达接受者之前,路由需要转发消息到目的习题,然后要求一个确认信息。这个确认信息可以给直接返回给网站或经过路由返回。也可以中介者修改了SOAP消息头或者增加了一个SOAP消息头块。实际上,一个中介者不应该修改消息或者删除消息头,除非消息是发给它的。基于这个模型,就可以轻易的创建一个包含包含路径记录的消息。每个中介者都可以增加它的SOAP消息头,直到消息到达最终的接受者,消息包含一串所有接触这个消息的中介者。如前所述,这个行为是建模在一个真实的邮政系统邮戳或者消息路由的例子上。

消息体

        消息体是必须的并且包含消息的有效部分。习惯上,消息体里的数据都是给最终的消息接受者的。不管中间有多少防火墙、路由或者中介者处理了这个SOAP消息。这个不是正式的规定。就像没有规定说邮局不能打开信件一样,同样不能保证一个中介者不打开和改变SOAP消息体。一种可能就是使用数字签名和加密来保证消息从初始发送者到最终接受者的完整性

       WCF支持SOAP, REST和 POX(朴素的旧的消息)。大部分目前的WCF应用编程接口(API),是使用SOAP消息结构的。毋庸质疑的是,将来会扩展消息结构,加入新的消息结构,比如JSON。

消息传输

  SOAP消息是独立于传输的。换句话说,没必要在消息里添加任何传输规范。这个简单的特性是许多关键特性之一,它使得这个消息结构无比强大。再说一次,用我们的邮政服务例子可以解释。如果一恶搞邮件依赖运输工具,就等于告诉邮递员你邮寄信件的地址,并且不包括信封上的信息。如果我们惯性思考,信件会紧紧地与邮递员绑在一起。这种紧密的耦合不好的几个原因如下:

  1. 消息可以只能被邮递员投递
  2. 其它邮政人员不能与消息交互(除非前一个邮递员告诉他)
  3. 批量分类和投递信件是困难的
  4.  因为没有信件返回地址,如果信件在处理的时候出现问题,发送者无法被通知。

    从面向服务的角度来看,这是一个糟糕的场景。一个更好的计划是它本身应该包含所有相关的地址信息,因此可以避免与传输层的机密耦合。当消息包含这些信息,许多SOAP行为(包括前面提到的行为)是可能的。例如,我们都知道邮件是被邮递员收取、转给分类工具并且通过飞机、火车、轮船或者汽车发送给其他的分类工具和别的邮递员。在我们日常的邮件例子里,可以看到在投递信件的时候运输可以改变(邮递员、分类器、飞机等等),这可以提高效率。如果没有地址的话,那些都不可能。