消息编码

来源:互联网 发布:恢复数据软件的标志 编辑:程序博客网 时间:2024/05/18 15:56


  消息编码

  随着时间的流逝,也许我们会条件反射式地认为XML(SOAP)是一个结构文本。毕竟,文本是人可读的,每个计算机系统也可以处理文本。基于文本的XML的普遍共性与我们的与其它系统交互的想法产生了共鸣。可以容易的解释的基于文本的XML本质上会体积变大。可以理解使用XML会带来性能损失。就像要花费点精力把信装到信封里一样,它需要一些处理时间与XML交互。某些情况下,基于文本的XML数据大小限制了它的应用,特别是当我们要通过网络发送一个XML消息的时候。

  此外,如果我们限制自己使用基于文本的XML,那么我们怎么才能在XML文档里发送二进制数据(像音乐或者视频)?如果你已经阅读了标准的XMLSchema数据类型,你会发现2个二进制数据类型:: xs:base64Binary 和xs:hexBinary。本质上说,两个数据类型都代表一组有序的8位字节。使用这些XML数据类型或许可以解决一些嵌入二进制数据到文档中的问题,但是事实上,这已经使得性能问题更加糟糕。众所周知的问题就是,base64编码会增加数据30%的大小。这个情况对于xs:hexBinary更坏,因为它会增加位原来的2倍大小。两个数据都是基于UTF-8编码的假设。如果我们采用UTF-16编码,这些倍数因子都会翻倍增加。

  XML 信息集( XML Infoset)

  为了找到性能的瓶颈的答案,我们详细来看一下XML文档的结构。如果我们看一下规范,XML是一个精确的撰写结构化数据的语法(定义在 http://www.w3.org/TR/REC-xml/)。它要求定义格式良好的XML文档必须包涵一个开始和结束元素、一个根节点等等。奇怪的是,XML规范发布以后,激起了抽象定义XML文档的需求。XML信息集(定义在http://www.w3.org/TR/xml-infoset/)提供了这个抽象定义。

  实际上,XML信息集定义是项目之间的关系,不定义任何具体的语法,我们能够解释许多不同的消息编码,包括一些比文本更高校的编码格式,而不需要修改我们的程序。

  SOAP和XML信息集

  记得SOAP是建立在XML之上的。这个产生一个问题:到底SOAP消息是建立在早期的XML语法上还是XML信息集上呢?答案是2者都有。2个SOAP规范并存:SOAP 1.1 和SOAP 1.2。SOAP1.1建立在旧的XML语法上,SOAP1.2建立在XML 信息集上。有这么一个事实,就可以猜想SOAP1.2建立的消息SOAP1.1的解析器可能无法阅读。WCF是建立在SOAP1.2(XML信息集上),但是它可以同时处理SOAP 1.1 和SOAP 1.2的消息。

  WCF可以用来和定制与其它实际的消息编码一起工作,只要消息是遵照SOAP1.1或者SOAP1.2的(它可以和不是SOAP消息一起工作)。如你将会在接下来的章节里看到的一样,WCF是一个可灵活接入和组合的架构。所以自定义编码器可以轻易地安装到WCF的管道上。当一个新的编码器开发完毕,微软或者第三方都可以在消息堆栈里创建和插入它。我将会在第6章:《通道》里详细介绍消息编码器。现在我们来看一下WCF里的编码器。在写本书的时候,WCF提供了三个编码器:文本(text)、(二进制)binary、 消息传输优化机制(Message Transmission Optimization Mechanism ,MTOM)。

  文本编码器

  和你从它的名字里猜到的一样,文本编辑器的输出结果是基于文本编码的消息。每个明白Unicode文本的系统都可以阅读和处理这个编码器传递来的消息,在与别的非WCF系统互操作时,这个是一个很帮的选择。二进制数据通过xs:base64Binary扩展样式定义(XSD)数据类型可以包涵到基于文本的消息里。这是一个使用WCF文本编码器编码过的消息(为了清晰,移除了一部分元素)。

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
    <s:Header></s:Header>
    <s:Body>
      <SubmitOrder xmlns="http://wintellect.com/OrderProcess">
        <Order xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
        <OrderByte xmlns="http://wintellect.com/Order">
mktjxwyxKr/9oW/jO48IhUwrZvNOdyuuquZEAIcy08aa+HXkT3dNmvE/
+zI96Q91a9Zb17HtrCIgtBwmbSk4ys2pSEMaIzXV3cwCD3z4ccDWzpWx1/
wUrEtSxJtaJi3HBzBlk6DMW0eghvnl652lKEJcUJ6Uh/LRlZz3x1+aereeOgdLkt4gCnNOEFECL8CtrJtY/taPM4A+k/
4E1JPnBgtCRrGWWpVkO0UqRXahz2XbShrDQnzgDwaHDf/
fHDXfZgpFwOgPF1IG88KQZO0JncSYKIp5I8OPYTeqD0yVhB8QSt9sWw59yzLHvU65UKoYfXA7RvOqZkJGtV6wZAgGcA=
=
          </OrderByte>
        <OrderNumber xmlns="http://wintellect.com/Order">
            12345
          </OrderNumber>
        </Order>
      </SubmitOrder>
    </s:Body>
 </s:Envelope>

  二进制编码器

  二进制编码器是最高效的消息编码器,并且只适用与WCF-到-WCF的通信。在WCF所有的编码器中,二进制编码器产生最小的消息。记住这个编码器产生一个序列化的信息集,即使它是二进制编码格式。将来可能,一个标准的二进制编码会被采用,这些编码的类型可以显著第改善消息应用的性能。

  MTOM编码器

  MTOM编码器根据MTOM规范创建消息。(MTOM规范可以在这里查到http://www.w3.org/TR/soap12-mtom/)因为MTOM编码已经规范化,所以其它厂商可以自由创建发送和接受消息的基础结构。结果,MTOM消息编码的WCF消息就可以发送给非WCF的应用(只要它们理解MTOM)。通常来说,MTOM为了允许高效第传输包涵二进制数据的消息,它也提供了数字签名。MTOM消息编码可以通过多用途网络邮件扩展协议(MIME)启用这些特性。MTOM消息的内容被XML-二进制优化包装方法所定义。更多信息:http://www.w3.org/TR/xop10/.

  在运行时,MTOM编码器为了数字签名创建一个基于base64编码的代表,让原始二进制数据可以在消息里打包。一个MTOM消息看起来如下:

// start of a boundary in the multipart message多部分消息的分界线
--uuid:+id=1
Content-ID: <http://wintellect.com/0>
Content-Transfer-Encoding: 8bit
// set the content type to xop+xml,设置内容类型xop+xml
Content-Type: application/xop+xml;charset=utf8; type="application/soap+xml"
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
 <s:Header></s:Header>
 <s:Body>
    <SubmitOrder xmlns="http://wintellect.com/OrderProcess">
      <order xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
        <OrderByte xmlns="http://wintellect.com/Order">
           // add a reference to another message part
          <xop:Include href=cid:http://wintellect.com/1/12345
           xmlns:xop="http://www.w3.org/2004/08/xop/include"/>
        </OrderByte>
        <OrderNumber xmlns="http://wintellect.com/Order">
          12345
       </OrderNumber>
      </order>
    </SubmitOrder>
 </s:Body>
</s:Envelope>
// end of the boundary in the first message part第一部分的内容结束边界
--uuid:+id=1
// add the binary data as an octect stream增加二进制数据为八位字节流
Content-ID: <http://wintellect.com/1/12345>
Content-Transfer-Encoding: binary
Content-Type: application/octet-stream
// raw binary data here这里是原始二进制数据

  注意到二进制数据被原样保坤在SOAP消息里的另一块区域里。因为二进制数据被打包到SOAP消息的外部区域,那么这么才能给SOAP进行数字签名呢?如果我们使用基于XML的安全机制,像XML加密和XML数字签名里描述的一样,我们不能引用外部的二进制流。这些加密个签名机制要求被保护的数据包装在SOAP消息里。咋一看,对于多部分的消息还真没有什么办法。事实上,这是直接网络消息封装(DIME)和SOAP附件的致命弱点。MTOM提供了一个有趣的解决办法。

  MTOM编码规范规定一个MTOM消息能够包涵二进制数据在base64编码的字符里,后者二进制流在额外的消息部分里。它也表示一个基于base64编码的二进制数据的代表在处理的时候必须可用。换句话说,额外的消息部分可以为消息传输创建,但是内联的base64数据必须对一些操作如:应用数字签名临时可用。当消息处于内联的基于base64编码的状态,基于XML编码的安全机制可以被应用到SOAP消息里。安全机制应用结束,消息可以被序列化为多部分消息。当接受者接受消息的时候,这个消息可以被XML安全规范机制强制根据一些列规则进行验证。

  非常有意思地看到,当大量二进制消息是基于base64编码或者二进制流编码在额外的消息部分的时候,WCF MTOM编码器能够正确地选择序列化。WCF编码器使用二进制数据大小作为选择的依据。在之前的消息里,OrderBytes元素大约800k。如果我们减少OrderBytes的大小到128k,在检查一下消息格式,我们可以看到:

// start of a boundary in the multipart message,多部分消息开始边界
--uuid:+id=1
Content-ID: <http://wintellect.com/0>
Content-Transfer-Encoding: 8bit
// set the content type to xop+xml
Content-Type: application/xop+xml;charset=utf8; type="application/soap+xml"
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
 <s:Header></s:Header>
 <s:Body>
    <SubmitOrder xmlns="http://wintellect.com/OrderProcess">
      <order xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
        <OrderByte xmlns="http://wintellect.com/Order">
kF+k2CQd/lCitSYvXnLhuOtaMCk/tZaFZIWeW7keC3YvgstAWoht/wiOiR5+HZPo+TzYoH+qE9vJHnSefqKXg6mw/
9ymoV1i7TEhsCt3BkfytmF9Rmv3hW7wdjsUzoBl9gZ1zR62QVjedbJNiWKvUhgtq8hAGjw+uXlttSohTh6xu7kkAjgoO
3QJntG4qfwMQCQj5iO4JdzJNhSkSYwtvCaTnM2oi0/fBHBUN3trhRB9YXQG/mj7+ZbdWsskg/
Lo2+GrJAwuY7XUROKyY+5hXrAEJ+cXJr6+mKM3yzCDu4B9bFuZv2ADTv6/MbmFSJWnfPwbH1wK0LQi7Ixo95iF
        </OrderByte>
        <OrderNumber xmlns="http://wintellect.com/Order">
          12345
        </OrderNumber>
      </order>
    </SubmitOrder>
 </s:Body>
</s:Envelope>
--uuid:+id=1-

  这个例子里,WCF编码器序列化二进制元素为基于base64编码的string。这个优化是相当符合MTOM规范。

  选择恰当的编码

  选择消息编码器强迫你去考虑当前和未来的消息使用问题。大部分来说,应用互操作性和消息里的数据类型会决定我们的选择。性能,在决定那个编码器是最适合我们系统的时候,也会考虑进来。表2-1基于消息类型和那种系统可以可以发送和接受消息列举了编码情况。


  表 2-1:消息编码器排列和场景 
 
  消息类型    Binary    Text    MTOM 
  Text内容, 只与WCF 交互    1    2    3 
  Text内容, 与现代非WCF系统交互   N/A    1    2 
  Text内容, 与旧的非WCF systems 交互   N/A    1    N/A 
  大二进制内容, 只与WCF 交互   1    3    2 
  大二进制内容, 与现代非WCF系统交互   N/A    2    1 
  大二进制内容, , 与旧的非WCF systems 交互   N/A    1    N/A 
  小二进制内容, 只与WCF 交互   1    2    3 
  小二进制内容, 与现代非WCF系统交互   N/A    1    2 
  小二进制内容, , 与旧的非WCF systems 交互   N/A    1    N/A 


  不应该惊讶,二进制编码器是WCF与其它WCF系统交互最高效的编码器。我们也许吃惊的是这个事实,在端到端的情况下,MTOM消息编码是比文本编码器效率还低。互操作性和二进制数据大小是你选择MTOM和文本编码器的因素。绝大多数情况,MTOM编码消息只能发送给MTOM编码器的系统。写本书的时候,MTOM是一个很新的规范,所以只有现代系统可以高效地处理MTOM消息。从性能的角度来看,MTOM编码器只有当包装到消息里的二进制数据很大的时候才有意义。MTOM不应该用在不包括二进制数据的消息里,因为MTOM的性能此时比文本编码器效率更低。因此,独立测试一个在产品环境里使用的消息非常重要。

  幸运的是,我们可以在第4章:WCF 101里看到,WCF就是这些编码选择都不需要大的改变应用程序的方式来设计的。事实上,这使得一个服务可以与多种不同的消息编码交互成为可能。举例来说,一个服务可以与两个二进制编码和文本编码的消息交互。这个场景的好处在于当与别的WCF参与者通信的时候,服务可以快速执行,并且可以与别的平台通信,比如JAVA。

 


There are verity of ways you can create messages (and these techniques are used by encoders). For example you can just specify a blob of xml in the memory or an instance of a serializable class like below.

[Serializable]
class Person
{
public string Name;
}

Person p = new Person();
p.Name = "abc";
message = Message.CreateMessage(MessageVersion.Default, "http://b.net/dosomething", p);

In this case if you look at the message in debugger you will see the whole content of the body.

You can also assign an XmlReader prepared to read Xml from a stream like this.

Message message = Message.CreateMessage(MessageVersion.Default, "http://b.net/dosomething",, XmlReader.Create("Sample.xml"));

In this case the message body contains the xml reader and you can actually look at the content by reading it using an xml reader. This xml reader can be obtained by calling Message.GetReaderAtBodyContents(); method.

In fact this is one of the reasons that WCF applications will scale quite well. By assigning a stream we can avoid the memory allocations required to buffer the message body. So you can transfer a 20Gig message without a problem (obviously the transport has to support streaming ;))…

Also note that in addition to pull mode message creation shown about WCF also supports push mode when creating the messages.

To do that you have create a body writer by inheriting the BodyWriter class override the OnWriteBodyContents method to push the desired data.

class MyBodyWriter : BodyWriter
{
public MessageBodyWriter(): base(true)
{
}

protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
{
writer.WriteStartElement("Products");
writer.WriteElementString("Product", "Test1");
writer.WriteElementString("Product", "Test2");
writer.WriteEndElement();
writer.Flush();
}
}

Then you can use an instance of your body writer when creating the message as follows:

message = Message.CreateMessage(MessageVersion.Default, "http://b.net/dosomething", new MyBodyWriter());

Note the Boolean parameter passed into the BodyWriter class constructor. It indicates whether the message is buffered or streamed. Switch this value and examine the message body in the debugger. You will see the light! J

Hope this helps!


  

原创粉丝点击