消息编码
随着时间的流逝,也许我们会条件反射式地认为
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>
<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这里是原始二进制数据
--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-
--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
|
幸运的是,我们可以在第
4
章:
WCF 101
里看到,
WCF
就是这些编码选择都不需要大的改变应用程序的方式来设计的。事实上,这使得一个服务可以与多种不同的消息编码交互成为可能。举例来说,一个服务可以与两个二进制编码和文本编码的消息交互。这个场景的好处在于当与别的
WCF
参与者通信的时候,服务可以快速执行,并且可以与别的平台通信,比如
JAVA
。
本文转自 frankxulei 51CTO博客,原文链接:http://blog.51cto.com/frankxulei/318626,如需转载请自行联系原作者