WCF消息压缩

对于WCF应用来说,传输前压缩请求消息和回复消息,不但可以降低网络流量,也可以提高网络传输的性能

一、消息压缩方案
二、用于数据压缩与解压缩组件
三、用于消息压缩与解压的组件
四、用于对请求/回复消息压缩和解压缩的组件
五、将CompressionMessageFormatter用于WCF运行时框架的操作行为
六、查看结构压缩后的消息
七、扩展

一、消息压缩方案
  消息压缩在WCF中的实现其实很简单,我们只需要在消息(请求消息/回复消息)被序列化之后,发送之前进行压缩;在接收之后,反序列化之前进行解压缩即可。针对压缩/解压缩使用的时机,有三种典型的解决方案。通过自定义MessageEncoder和MessageEncodingBindingElement 来完成。

1.将编码后的字节流压缩传输

2.创建用于压缩和解压缩的信道

3. 自定义MessageFormatter实现序列化后的压缩和法序列化前的解压缩

这里要介绍的解决方案3。  

二、用于数据压缩与解压缩组件
  我们支持两种方式的压缩,Dflate和GZip。两种不同的压缩算法通过如下定义的CompressionAlgorithm枚举表示。

1     public enum CompressionAlgorithm
2     {
3         GZip,
4         Deflate
5     }

而如下定义的DataCompressor负责基于上述两种压缩算法实际上的压缩和解压缩工作。

 1     internal class DataCompressor
 2     {
 3         public static byte[] Compress(byte[] decompressedData, CompressionAlgorithm algorithm)
 4         {
 5             using (MemoryStream stream = new MemoryStream())
 6             {
 7                 if (algorithm == CompressionAlgorithm.Deflate)
 8                 {
 9                     GZipStream stream2 = new GZipStream(stream, CompressionMode.Compress, true);
10                     stream2.Write(decompressedData, 0, decompressedData.Length);
11                     stream2.Close();
12                 }
13                 else
14                 {
15                     DeflateStream stream3 = new DeflateStream(stream, CompressionMode.Compress, true);
16                     stream3.Write(decompressedData, 0, decompressedData.Length);
17                     stream3.Close();
18                 }
19                 return stream.ToArray();
20             }
21         }
22
23         public static byte[] Decompress(byte[] compressedData, CompressionAlgorithm algorithm)
24         {
25             using (MemoryStream stream = new MemoryStream(compressedData))
26             {
27                 if (algorithm == CompressionAlgorithm.Deflate)
28                 {
29                     using (GZipStream stream2 = new GZipStream(stream, CompressionMode.Decompress))
30                     {
31                         return LoadToBuffer(stream2);
32                     }
33                 }
34                 else
35                 {
36                     using (DeflateStream stream3 = new DeflateStream(stream, CompressionMode.Decompress))
37                     {
38                         return LoadToBuffer(stream3);
39                     }
40                 }
41             }
42         }
43
44         private static byte[] LoadToBuffer(Stream stream)
45         {
46             using (MemoryStream stream2 = new MemoryStream())
47             {
48                 int num;
49                 byte[] buffer = new byte[0x400];
50                 while ((num = stream.Read(buffer, 0, buffer.Length)) > 0)
51                 {
52                     stream2.Write(buffer, 0, num);
53                 }
54                 return stream2.ToArray();
55             }
56         }
57     }

三、用于消息压缩与解压的组件  

  而针对消息的压缩和解压缩通过如下一个MessageCompressor来完成。具体来说,我们通过上面定义的DataCompressor对消息的主体部分内容进行压缩,并将压缩后的内容存放到一个预定义的XML元素中(名称和命名空间分别为CompressedBody和http://www.yswenli.net/comporession/),同时添加相应的MessageHeader表示消息经过了压缩,以及采用的压缩算法。对于解压缩,则是通过消息是否具有相应的MessageHeader判断该消息是否经过压缩,如果是则根据相应的算法对其进行解压缩。

具体的实现如下:

 1     public class MessageCompressor
 2     {
 3         public MessageCompressor(CompressionAlgorithm algorithm)
 4         {
 5             this.Algorithm = algorithm;
 6         }
 7         public Message CompressMessage(Message sourceMessage)
 8         {
 9             byte[] buffer;
10             using (XmlDictionaryReader reader1 = sourceMessage.GetReaderAtBodyContents())
11             {
12                 buffer = Encoding.UTF8.GetBytes(reader1.ReadOuterXml());
13             }
14             if (buffer.Length == 0)
15             {
16                 Message emptyMessage = Message.CreateMessage(sourceMessage.Version, (string)null);
17                 sourceMessage.Headers.CopyHeadersFrom(sourceMessage);
18                 sourceMessage.Properties.CopyProperties(sourceMessage.Properties);
19                 emptyMessage.Close();
20                 return emptyMessage;
21             }
22             byte[] compressedData = DataCompressor.Compress(buffer, this.Algorithm);
23             string copressedBody = CompressionUtil.CreateCompressedBody(compressedData);
24             XmlTextReader reader = new XmlTextReader(new StringReader(copressedBody), new NameTable());
25             Message message2 = Message.CreateMessage(sourceMessage.Version, null, (XmlReader)reader);
26             message2.Headers.CopyHeadersFrom(sourceMessage);
27             message2.Properties.CopyProperties(sourceMessage.Properties);
28             message2.AddCompressionHeader(this.Algorithm);
29             sourceMessage.Close();
30             return message2;
31         }
32
33         public Message DecompressMessage(Message sourceMessage)
34         {
35             if (!sourceMessage.IsCompressed())
36             {
37                 return sourceMessage;
38             }
39             CompressionAlgorithm algorithm = sourceMessage.GetCompressionAlgorithm();
40             sourceMessage.RemoveCompressionHeader();
41             byte[] compressedBody = sourceMessage.GetCompressedBody();
42             byte[] decompressedBody = DataCompressor.Decompress(compressedBody, algorithm);
43             string newMessageXml = Encoding.UTF8.GetString(decompressedBody);
44             XmlTextReader reader2 = new XmlTextReader(new StringReader(newMessageXml));
45             Message newMessage = Message.CreateMessage(sourceMessage.Version, null, reader2);
46             newMessage.Headers.CopyHeadersFrom(sourceMessage);
47             newMessage.Properties.CopyProperties(sourceMessage.Properties);
48             return newMessage;
49         }
50         public CompressionAlgorithm Algorithm { get; private set; }
51     }

下面是针对Message类型而定义了一些扩展方法和辅助方法。

 1     public static class CompressionUtil
 2     {
 3         public const string CompressionMessageHeader = "Compression";
 4         public const string CompressionMessageBody = "CompressedBody";
 5         public const string Namespace = "http://www.yswenli.net/compression";
 6
 7         public static bool IsCompressed(this Message message)
 8         {
 9             return message.Headers.FindHeader(CompressionMessageHeader, Namespace) > -1;
10         }
11
12         public static void AddCompressionHeader(this Message message, CompressionAlgorithm algorithm)
13         {
14             message.Headers.Add(MessageHeader.CreateHeader(CompressionMessageHeader, Namespace, string.Format("algorithm = \"{0}\"", algorithm)));
15         }
16
17         public static void RemoveCompressionHeader(this Message message)
18         {
19             message.Headers.RemoveAll(CompressionMessageHeader, Namespace);
20         }
21
22         public static CompressionAlgorithm GetCompressionAlgorithm(this Message message)
23         {
24             if (message.IsCompressed())
25             {
26                 var algorithm = message.Headers.GetHeader<string>(CompressionMessageHeader, Namespace);
27                 algorithm = algorithm.Replace("algorithm =", string.Empty).Replace("\"", string.Empty).Trim();
28                 if (algorithm == CompressionAlgorithm.Deflate.ToString())
29                 {
30                     return CompressionAlgorithm.Deflate;
31                 }
32
33                 if (algorithm == CompressionAlgorithm.GZip.ToString())
34                 {
35                     return CompressionAlgorithm.GZip;
36                 }
37                 throw new InvalidOperationException("Invalid compression algrorithm!");
38             }
39             throw new InvalidOperationException("Message is not compressed!");
40         }
41
42         public static byte[] GetCompressedBody(this Message message)
43         {
44             byte[] buffer;
45             using (XmlReader reader1 = message.GetReaderAtBodyContents())
46             {
47                 buffer = Convert.FromBase64String(reader1.ReadElementString(CompressionMessageBody, Namespace));
48             }
49             return buffer;
50         }
51
52         public static string CreateCompressedBody(byte[] content)
53         {
54             StringWriter output = new StringWriter();
55             using (XmlWriter writer2 = XmlWriter.Create(output))
56             {
57                 writer2.WriteStartElement(CompressionMessageBody, Namespace);
58                 writer2.WriteBase64(content, 0, content.Length);
59                 writer2.WriteEndElement();
60             }
61             return output.ToString();
62         }
63     }

四、用于对请求/回复消息压缩和解压缩的组件  

  消息的序列化和反序列化最终是通过MessageFormatter来完成的。具体来说,客户端通过ClientMessageFormatter实现对请求消息的序列化和对回复消息的序列化,而服务端通过DispatchMessageFormatter实现对请求消息的反序列化和对回复消息的序列化。

  在默认的情况下,WCF选用的MessageFormatter为DataContractSerializerOperationFormatter,它采用DataContractSerializer进行实际的序列化和法序列化操作。我们自定义的MessageFormatter实际上是对DataContractSerializerOperationFormatter的封装,我们依然使用它来完成序列化和反序列化工作,额外实现序列化后的压缩和法序列化前的解压缩。

  因为DataContractSerializerOperationFormatter是一个internal类型,我们只有通过反射的方式来创建它。如下的代码片断为用于进行消息压缩与解压缩的自定义MessageFormatter,即CompressionMessageFormatter的定义。

 1     public class CompressionMessageFormatter : IDispatchMessageFormatter, IClientMessageFormatter
 2     {
 3         private const string DataContractSerializerOperationFormatterTypeName = "System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
 4
 5         public IDispatchMessageFormatter InnerDispatchMessageFormatter { get; private set; }
 6         public IClientMessageFormatter InnerClientMessageFormatter { get; private set; }
 7         public MessageCompressor MessageCompressor { get; private set; }
 8
 9         public CompressionMessageFormatter(CompressionAlgorithm algorithm, OperationDescription description, DataContractFormatAttribute dataContractFormatAttribute, DataContractSerializerOperationBehavior serializerFactory)
10         {
11             this.MessageCompressor = new MessageCompressor(algorithm);
12             Type innerFormatterType = Type.GetType(DataContractSerializerOperationFormatterTypeName);
13             var innerFormatter = Activator.CreateInstance(innerFormatterType, description, dataContractFormatAttribute, serializerFactory);
14             this.InnerClientMessageFormatter = innerFormatter as IClientMessageFormatter;
15             this.InnerDispatchMessageFormatter = innerFormatter as IDispatchMessageFormatter;
16         }
17
18         public void DeserializeRequest(Message message, object[] parameters)
19         {
20             message = this.MessageCompressor.DecompressMessage(message);
21             this.InnerDispatchMessageFormatter.DeserializeRequest(message, parameters);
22         }
23
24         public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
25         {
26             var message = this.InnerDispatchMessageFormatter.SerializeReply(messageVersion, parameters, result);
27             return this.MessageCompressor.CompressMessage(message);
28         }
29
30         public object DeserializeReply(Message message, object[] parameters)
31         {
32             message = this.MessageCompressor.DecompressMessage(message);
33             return this.InnerClientMessageFormatter.DeserializeReply(message, parameters);
34         }
35
36         public Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
37         {
38             var message = this.InnerClientMessageFormatter.SerializeRequest(messageVersion, parameters);
39             return this.MessageCompressor.CompressMessage(message);
40         }
41     }

五、将CompressionMessageFormatter用于WCF运行时框架的操作行为  

  ClientMessageFormatter和DispatchMessageFormatter实际上属于ClientOperation和DispatchOperation的组件。我们可以通过如下一个自定义的操作行为CompressionOperationBehaviorAttribute将其应用到相应的操作上。

 1     [AttributeUsage(AttributeTargets.Method)]
 2     public class CompressionOperationBehaviorAttribute : Attribute, IOperationBehavior
 3     {
 4         public CompressionAlgorithm Algorithm { get; set; }
 5
 6         public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) { }
 7
 8         public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
 9         {
10             clientOperation.SerializeRequest = true;
11             clientOperation.DeserializeReply = true;
12             var dataContractFormatAttribute = operationDescription.SyncMethod.GetCustomAttributes(typeof(DataContractFormatAttribute), true).FirstOrDefault() as DataContractFormatAttribute;
13             if (null == dataContractFormatAttribute)
14             {
15                 dataContractFormatAttribute = new DataContractFormatAttribute();
16             }
17
18             var dataContractSerializerOperationBehavior = operationDescription.Behaviors.Find<DataContractSerializerOperationBehavior>();
19             clientOperation.Formatter = new CompressionMessageFormatter(this.Algorithm, operationDescription, dataContractFormatAttribute, dataContractSerializerOperationBehavior);
20         }
21
22         public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
23         {
24             dispatchOperation.SerializeReply = true;
25             dispatchOperation.DeserializeRequest = true;
26             var dataContractFormatAttribute = operationDescription.SyncMethod.GetCustomAttributes(typeof(DataContractFormatAttribute), true).FirstOrDefault() as DataContractFormatAttribute;
27             if (null == dataContractFormatAttribute)
28             {
29                 dataContractFormatAttribute = new DataContractFormatAttribute();
30             }
31             var dataContractSerializerOperationBehavior = operationDescription.Behaviors.Find<DataContractSerializerOperationBehavior>();
32             dispatchOperation.Formatter = new CompressionMessageFormatter(this.Algorithm, operationDescription, dataContractFormatAttribute, dataContractSerializerOperationBehavior);
33         }
34
35         public void Validate(OperationDescription operationDescription) { }
36     }

六、查看结构压缩后的消息
为了验证应用了CompressionOperationBehaviorAttribute特性的操作方法对应的消息是否经过了压缩,我们可以通过一个简单的例子来检验。我们采用常用的计算服务的例子,下面是服务契约和服务类型的定义。我们上面定义的CompressionOperationBehaviorAttribute应用到服务契约的Add操作上。

 1     [ServiceContract(Namespace = "http://www.yswenli.net/")]
 2     public interface ICalculator
 3     {
 4         [OperationContract]
 5         [CompressionOperationBehavior]
 6         double Add(double x, double y);
 7     }
 8     public class CalculatorService : ICalculator
 9     {
10         public double Add(double x, double y)
11         {
12             return x + y;
13         }
14     }

我们采用BasicHttpBinding作为终结点的绑定类型(具体的配置请查看源代码),下面是通过Fiddler获取的消息的内容,它们的主体部分都经过了基于压缩的编码。

1     <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
2       <s:Header>
3         <Compression xmlns="http://www.yswenli.net/compression">algorithm = "GZip"</Compression>
4       </s:Header>
5       <s:Body>
6         <CompressedBody xmlns="http://www.yswenli.net/compression">7L0HYBx ... CQAA//8=</CompressedBody>
7       </s:Body>
8     </s:Envelope>

回复消息

1     <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
2       <s:Header>
3         <Compression xmlns="http://www.yswenli.net/compression">algorithm = "GZip"</Compression>
4       </s:Header>
5       <s:Body>
6         <CompressedBody xmlns="http://www.yswenli.net/compression">7L0H...PAAAA//8=</CompressedBody>
7       </s:Body>
8     </s:Envelope>

七、扩展

如果不想使微软自带的序列化或者因为某些原因(emoji字符异常等)可以使用自定义的IDispatchMessageInspector。由于CompressionMessageFormatter使用基于DataContractSerializer序列化器的DataContractSerializerOperationFormatter进行消息的序列化和发序列化工作,而DataContractSerializer仅仅是WCF用于序列化的一种默认的选择(WCF还可以采用传统的XmlSeriaizer);为了让CompressionMessageFormatter能够使用其他序列化器,可以对于进行相应的修正。

转载请标明本文来源:http://www.cnblogs.com/yswenli/p/6670081.html
更多内容欢迎我的的github:https://github.com/yswenli
如果发现本文有什么问题和任何建议,也随时欢迎交流~

 
时间: 2024-10-29 19:05:45

WCF消息压缩的相关文章

《Artech的WCF剖析系列》系列技术文章整理收藏

<Artech的WCF剖析系列>系列技术文章整理收藏 1控制并发访问的三道屏障: WCF限流(Throttling)体系探秘[下篇] 2使命必达: 深入剖析WCF的可靠会话[实例篇](内含美女图片,定力差者慎入) 3使命必达: 深入剖析WCF的可靠会话[概念篇] 4WCF如何克服HTTP传输协议的局限提供对不同消息传输模式的实现 5WCF中并发(Concurrency)与限流(Throttling)体系深入解析系列[共7篇] 6使命必达: 深入剖析WCF的可靠会话[协议篇](上) 7使命必达:

.NET4.5中WCF中默认生成的basicHttpsBinding的研究

起因: 使用.net4.5建立了一个空白的WCF服务.默认使用的绑定配置是basicHttpsBinding. 问题发现: 1.用客户端进行服务引用,生成了默认的配置文件,其中绑定配置是basicHttpBinding. 2.因为需要单次传递大批量数据,所以修改绑定配置,如下: <basicHttpBinding> <binding name="BasicHttpBinding_IService" closeTimeout="00:10:00" r

PHP调用WCF提供的方法

一.准备工作 1.安装wampserver:过程略 2.配置wampserver: 2.1打开php.ini文件,去掉 ;extension=php_soap.dll 这里那个分号. 也有说把这个 ;extension=php_openssl.dll前面的分号也去掉的. 2.2 如上图,将php_soap打上√. 2.3 如上图,打开httpd.conf文件,找到Listen 80 ,将80端口改成一个较大的端口,如8000.因为80端口也能别的程序用着. 在这个directory里面有php页

WCF服务部署到IIS7.5

下面介绍如何把WCF服务部署到IIS: 为WCF服务创建.svc文件 我们知道,每一个ASP.NET Web服务都具有一个.asmx文本文件,客户端通过访问.asmx文件实现对相应Web服务的调用.与之类似,每个WCF服务也具有一个对应的文本文 件,其文件扩展名为.svc.基于IIS的服务寄宿要求相应的WCF服务具有相应的.svc文件,.svc文件部署于IIS站点中,对WCF服务的调用体 现在对.svc文件的访问上. .svc文件的内容很简单,仅仅包含一个ServiceHost指令(Direct

WCF学习之旅----正式篇之基础框架

服务类包括服务契约IWCFService.操作契约OperationContract.和数据契约DataContract. using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel; using System.Text; using System.ServiceModel.Description; using System.Runtime.Serialization;

WCF入门(一)--Request Entity Too large 传输的数据量过大

通过WCF进行数据的查询或者添加的时候,如果数据量过大,一般会报出如下的错误: 1.已超过传入消息(65536)的最大消息大小配额.若要增加配额,请使用相应绑定元素上的MaxReceivedMessageSize 属性. 2.远程服务器返回了意外反应(413)Request Entity too large. 3.远程服务器返回了意外反应(400)Bad Request. 具体的解决方案: 服务端返回数据给客户端报错 在客户端的配置文件中,主要修改maxReceivedMessageSize <

Wcf体现Restful风格

Wcf体现Restful风格 概述 含状态传输(Representational State Transfer)的软件架构风格.主要特点 1.  资源是由URI来指定: 例如http://example.com/resources/ 2.  对资源的操作 包括获取.创建.修改和删除资源,这些操作正好对应HTTP协议提供的GET.POST.PUT和DELETE方法 3.  传输的资源:Web服务接受与返回的互联网媒体类型,比如:JSON,XML ,YAML 等. 下面通过一个简单的例子逐个问题解决

转 WCF WebService区别

下面我们来详细讨论一下二者的区别.Web Service和WCF的到底有什么区别. [1]Web Service:严格来说是行业标准,也就是Web Service 规范,也称作WS-*规范,既不是框架,也不是技术. 它有一套完成的规范体系标准,而且在持续不断的更新完善中. 它使用XML扩展标记语言来表示数据(这个是夸语言和平台的关键).微软的Web服务实现称为ASP.NET Web Service.它使用Soap简单对象访问协议来实现分布式环境里应用程序之间的数据交互.WSDL来实现服务接口相关

WCF中数据契约之已知类型的几种公开方式

WCF中传输的数据不想传统的面向对象编程,它只传递了一些对象的属性,但是自身并不知道自己属于什么对象,所以,他没有子类和父类的概念,因而也就没有Is-a的关系,所以在WCF中,如果想维持这种继承关系,就需要做一些特殊的处理了. 假设有如下定义, namespace KnownTypeExampleInterface{    [DataContract]    public class Employee    {        [DataMember]        public string N