博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
WCF 消息压缩性能问题及解决方法
阅读量:4582 次
发布时间:2019-06-09

本文共 10878 字,大约阅读时间需要 36 分钟。

最近使用WCF作为通迅框架开发一套信息系统,系统使用传统C/S框架,系统有可能会部署在互联网上,因此决定对传输的数据进行GZIP压缩,原来在使用.NET Remoting时,可以使用插入自定义的ChannelSink来实现数据压缩,作为.NET Remoting的替代方案的WCF,实现起来也很容易,且方法不止一种,主要解决方法主要有以下四种:

  • 通过自定义MessageEncoder和MessageEncodingBindingElement 来完成。具体的实现,可以参阅张玉彬的文章《》和MSDN的文章《》。
  • 直接创建用于压缩和解压缩的信道,在CodePlex中具有这么一个;
  • 自定义MessageFormatter实现序列化后的压缩和反序列化前的解压,详见WCF大师中的博客有《》
  • 自定义MessageInspector实现这就是我们今天将要讨论的解决方案。

相比较,第三和第四实现相对简单,配置很简单,它们的内部实现方法很类似,我的消息压缩类也来源于WCF大师的博客《》的消息压缩类,区别在于第三在自定义MessageFormatter中对消息进行压缩和解压缩,而第四是在自定义MessageInspector中对消息进行压缩和解压缩。下面给出第四种实现方法(网络上也很多):

一、Compress-压缩与解压缩类

///     /// 压缩解压缩类    ///     public class Compress    {        public static byte[] Zip(byte[] sourceBytes)        {            using (MemoryStream mStream = new MemoryStream())            {                GZipStream gStream = new GZipStream(mStream, CompressionMode.Compress);                gStream.Write(sourceBytes, 0, sourceBytes.Length);                gStream.Close();                return mStream.ToArray();            }        }        public static byte[] UnZip(byte[] sourceBytes)        {            using (MemoryStream mStream = new MemoryStream())            {                using (GZipStream gStream = new GZipStream(new MemoryStream(sourceBytes), CompressionMode.Decompress))                {                    int readBytes = 0;                    byte[] buffer = new byte[1024];                    while ((readBytes = gStream.Read(buffer, 0, buffer.Length)) > 0)                    {                        mStream.Write(buffer, 0, readBytes);                    }                    return mStream.ToArray();                }            }        }  }
压缩与解压缩类

二、MessageCompressor—消息压缩与解压类

///     /// 消息压缩类    ///     public static class MessageCompress    {        public static string Namespace = "http://myjece";        public static Message CompressMessage(Message sourceMessage)        {            byte[] buffer;            string sourceBody;            using (XmlDictionaryReader reader1 = sourceMessage.GetReaderAtBodyContents())            {                sourceBody = reader1.ReadOuterXml();                buffer = Encoding.UTF8.GetBytes(sourceBody);            }            XmlTextReader reader;            if (buffer.Length > 256)            {                byte[] compressedData = Compress.Zip(buffer);                string compressedBody = CreateCompressedBody(compressedData);                reader = new XmlTextReader(new StringReader(compressedBody), new NameTable());                sourceMessage.AddCompressionHeader();            }            else            {                reader = new XmlTextReader(new StringReader(sourceBody), new NameTable());            }            Message message = Message.CreateMessage(sourceMessage.Version, null, (XmlReader)reader);            message.Headers.CopyHeadersFrom(sourceMessage);            message.Properties.CopyProperties(sourceMessage.Properties);            sourceMessage.Close();            return message;        }        public static Message DeCompressMessage(Message sourceMessage)        {            if (!sourceMessage.IsCompressed())            {                return sourceMessage;            }            else            {                sourceMessage.RemoveCompressionHeader();                string deCompressedBody = Encoding.UTF8.GetString(Compress.UnZip(sourceMessage.GetCompressedBody()));                XmlTextReader reader = new XmlTextReader(new StringReader(deCompressedBody), new NameTable());                Message message = Message.CreateMessage(sourceMessage.Version, null, (XmlReader)reader);                message.Headers.CopyHeadersFrom(sourceMessage);                message.Properties.CopyProperties(sourceMessage.Properties);                message.AddCompressionHeader();                //sourceMessage.Close();                return message;            }        }        public static bool IsCompressed(this Message message)        {            return message.Headers.FindHeader("Compression", Namespace) > -1;        }        public static void AddCompressionHeader(this Message message)        {            message.Headers.Add(MessageHeader.CreateHeader("Compression", Namespace, "GZip"));        }        public static void RemoveCompressionHeader(this Message message)        {            message.Headers.RemoveAll("Compression", Namespace);        }        public static string CreateCompressedBody(byte[] content)        {            StringWriter output = new StringWriter();            using (XmlWriter writer2 = XmlWriter.Create(output))            {                writer2.WriteStartElement("CompressedBody", Namespace);                writer2.WriteBase64(content, 0, content.Length);                writer2.WriteEndElement();            }            return output.ToString();        }        public static byte[] GetCompressedBody(this Message message)        {            byte[] buffer;            using (XmlReader reader1 = message.GetReaderAtBodyContents())            {                buffer = Convert.FromBase64String(reader1.ReadElementString("CompressedBody", Namespace));            }            return buffer;        }    }
消息压缩与解压缩类

三、ClientCompressionInspector-客户端对消息进行压缩与解压缩的消息检查器

private class ClientCompressionInspector : IClientMessageInspector        {            #region IClientMessageInspector Members            public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)            {                reply = MessageCompress.DeCompressMessage(reply);            }            public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)            {                //加入一个消息头,表明客户端支持gzip消息压缩与解压缩                request.Headers.Add(MessageHeader.CreateHeader("AcceptEncoding", "http://myjece", "gzip"));                request = MessageCompress.CompressMessage(request);                return null;            }            #endregion        }
客户端对消息进行压缩与解压缩的消息检查器

 

public class ClientCompressionBehavior : BehaviorExtensionElement, IEndpointBehavior    {        public override Type BehaviorType        {            get            {                return typeof(ClientCompressionBehavior);            }        }        protected override object CreateBehavior()        {            return new ClientCompressionBehavior();        }        #region IEndpointBehavior Members        public void AddBindingParameters(ServiceEndpoint endpoint,            System.ServiceModel.Channels.BindingParameterCollection bindingParameters)        {            return;        }        public void ApplyClientBehavior(ServiceEndpoint endpoint,            System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)        {            clientRuntime.MessageInspectors.Add(new ClientCompressInspector());        }        public void ApplyDispatchBehavior(ServiceEndpoint endpoint,            System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)        {        }        public void Validate(ServiceEndpoint endpoint)        {            return;        }   }
客户端用于插入压缩消息检查器的终节点行为器

四、ServiceCompressInspector-服务端对消息进行压缩与解压缩的消息检查器

public class ServiceCompressInspector : IDispatchMessageInspector    {            public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)        {            request = MessageCompress.DeCompressMessage(request);            return null;        }        public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)        {            if (GetHeader("AcceptEncoding") == "gzip")            {                reply = MessageCompress.CompressMessage(reply);            }        }        public static string GetHeader(string headerName)        {            if (OperationContext.Current.IncomingMessageHeaders.FindHeader(headerName, "http://myjece") >= 0)                {                    return OperationContext.Current.IncomingMessageHeaders.GetHeader
(headerName, "http://myjece"); } else { return null; } } }
服务端对消息进行压缩与解压缩的消息检查器
public class ServiceCompressBehavior : BehaviorExtensionElement, IServiceBehavior    {        public void AddBindingParameters(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection
endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) { //throw new NotImplementedException(); } public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase) { foreach (ChannelDispatcher chDisp in serviceHostBase.ChannelDispatchers) { foreach (EndpointDispatcher epDisp in chDisp.Endpoints) { epDisp.DispatchRuntime.MessageInspectors.Add(new ServiceCompressInspector()); } } } public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase) { //throw new NotImplementedException(); } public override Type BehaviorType { get { return typeof(ServiceCompressBehavior); } } protected override object CreateBehavior() { return new ServiceCompressBehavior(); } }
服务端用于插入压缩消息检查器和服务端行为器

五、服务端配置

在system.serviceModel节点下添加:

好了,上面的基本的实现,可以通过在客户端行为器中添加AcceptEncoding=gzip的消息头来确定服务端返回的消息是否也可以(需要)进行压缩,实际运行结果也正常,但后来发现在进行大量byte[]类型数据传输时,发现有延时,几百K有数据,在局域网(排除网络问题)内,尽然达到2秒左右延时,开始怀疑的GZIP压缩类有问题,后发现,压缩类的对数据进行压缩时,耗时极小,一般几毫秒到几十毫秒之间,最后,只能逐语句进行排查,发现问题在消息压缩类中的:

using (XmlDictionaryReader reader1 = sourceMessage.GetReaderAtBodyContents())            {                sourceBody = reader1.ReadOuterXml();                buffer = Encoding.UTF8.GetBytes(sourceBody);            }

Message在经过每一层消息检查器时,都是以Message方法进行传递,只有到达TransportBinding上编码器时,才会对Message进行编码,或文本,或二进制,但在我上面的消息压缩中,先从原Message中获取Body内容,然后对Body进行压缩,再把压缩Body封装进新的Message中,问题就出在获取Body内容中,XmlDictionaryReader 的ReadOuterXml()方法相当对Body进行了XML的编码,所以导致了性能问题。解决问题的根本在于找到一个能获取到Body内容,又能避免提前对Body内容进行XML编码的方法。 

将上述代码改为以下代码后,性能得以大幅提升:

MemoryStream ms = new MemoryStream();            XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(ms, Encoding.UTF8);            sourceMessage.WriteBodyContents(writer);            writer.Flush();            buffer=ms.ToArray();

但最终获取到的buffer是一致的,是什么原因导致它们之间有巨大的性能差异就不得而知了……

 

 

 

转载于:https://www.cnblogs.com/myjece/p/3269980.html

你可能感兴趣的文章
.net core 无法获取本地变量或参数的值,因为它在此指令指针中不可用,可能是因为它已经被优化掉了...
查看>>
Poj2186Popular Cows
查看>>
TCP之listen&backlog
查看>>
实验室的毕业照
查看>>
核心编程答案(第六章)
查看>>
Spring 3.x jar 包详解 与 依赖关系
查看>>
java线程详解二
查看>>
maven项目导入依赖jar包并打包为可运行的jar包
查看>>
leecode第二十三题(合并K个排序链表)
查看>>
关于Eclipse的unsupported major minor version 51.0 错误
查看>>
2014年目标
查看>>
weblogic启动后 登陆控制台特别慢的问题
查看>>
Spring加载resource时classpath*:与classpath:的区别
查看>>
映射“DataAdapter.TableMappings”
查看>>
activity生命周期
查看>>
动画学习之Music图形绘制
查看>>
2019 2.15模拟赛
查看>>
基于H5 pushState实现无跳转页面刷新
查看>>
关于同余与模运算的总结
查看>>
js中top、clientTop、scrollTop、offsetTop的区别 文字详细说明版
查看>>