在客户端和服务器之间传输大量数据时,通常会面临性能瓶颈。默认的二进制序列化器效率低下,导致通信过程中出现大量冗余数据,从而引发远程调用错误和性能下降。为了解决这个问题,可以采用全局解决方案,通过压缩远程请求和响应来减轻超过远程最大请求大小的问题。本文将深入探讨远程通信的可扩展架构,并详细介绍压缩实现的细节。
一种可能的设计方法是围绕远程通信的可扩展架构和通道的定制来构建。通道在发送或接收消息之前或之后,会通过一系列通道接收器对象链来发送每个消息。默认情况下,接收器链包含通道基本功能所需的接收器,例如代理、格式化器和传输。通道接收器链处理发送到或从应用程序域接收的消息,并且可以通过插入自定义处理接收器来增强这个过程,例如,这里的自定义压缩接收器,后续处理将使用处理后返回给系统的消息。
服务器和客户端通道都可以配置为使用压缩,无论是通过编程方式还是使用配置文件。客户端压缩接收器是通过两个类实现的,一个是提供者,
CompressionClientChannelSinkProvider
,
另一个是接收器,
CompressionClientChannelSink
。
对于服务器端,接收器也是通过两个类实现的,一个是提供者,
CompressionServerChannelSinkProvider
,
另一个是接收器,
CompressionServerChannelSink
。
接下来,将回顾一个带有自定义压缩接收器的示例代码,该代码代表了一个典型的使用分层架构设计的应用程序,其中服务层负责使用请求/响应设计模式与其他层进行通信。
创建了一个示例应用程序,它使用了压缩通道接收器。下面定义了一个从泛型
ServiceBase<t>
类派生的示例请求对象:
[Serializable]
public class ConcreteTypeRequest : ServiceBase<concretetype>
{
private int _userId;
public int UserId
{
get { return _userId; }
set { _userId = value; }
}
public ConcreteTypeRequest() {}
}
接下来,服务器配置文件和客户端配置文件应该包含服务器和客户端自定义压缩接收器的设置。以下是使用远程可扩展框架的堆叠提供者配置文件的示例。最重要的是在
serverProvider
部分中指定提供者和默认格式化器的顺序。它还提供了一个压缩阈值参数,允许以字节为单位指定阈值:
<configuration>
<system.runtime.Remoting>
<application>
<service>
<wellknown type="Business.BusinessManager, Business" objectUri="BusinessManager.rem" mode="SingleCall" />
</service>
<channels>
<channel ref="tcp" port="5000">
<serverProviders>
<provider type="Util.CompressionServerChannelSinkProvider, Util" compressionThreshold="500000" />
<formatter ref="binary" />
</serverProviders>
</channel>
</channels>
</application>
</system.runtime.Remoting>
</configuration>
<configuration>
<system.runtime.Remoting>
<application>
<client>
<wellknown type="Business.BusinessManager, Business" objectUri="BusinessManager.rem" url="tcp://localhost:5000/BusinessManager.rem" />
</client>
<channels>
<channel ref="tcp">
<clientProviders>
<formatter ref="binary" />
<provider type="Util.CompressionClientChannelSinkProvider, Util" compressionThreshold="500000" />
</clientProviders>
</channel>
</channels>
</application>
</system.runtime.Remoting>
</configuration>
默认情况下,压缩对AppDomain中的所有请求和响应都是启用的。如果请求大小(以字节为单位)低于阈值,则在传输时不进行压缩。如果它大于阈值,在案例中等于500000字节,它将在传输到服务器之前被压缩。如果想要退出,应该在类上指定
[NonCompressible]
属性,如下所示:
[Serializable]
[NonCompressible]
public class ConcreteTypeRequest : ServiceBase<concretetype>
{
private int _userId;
public int UserId
{
get { return _userId; }
set { _userId = value; }
}
public ConcreteTypeRequest() {}
}
此外,
[NonCompressible]
属性是无条件的,因此永久禁用此类请求的压缩。这在企业中的某些场景中可能是有用的,例如,当请求的有效载荷已经被压缩时。如果想要在某些实例上有条件地应用压缩,请求可以实现一个
ICompressible
接口,如下所示:
[Serializable]
public class ConcreteTypeRequest : ServiceBase<concretetype>, ICompressible
{
private int _userId;
public int UserId
{
get { return _userId; }
set { _userId = value; }
}
public ConcreteTypeRequest() {}
#region ICompressible Members
public bool PerformCompression()
{
return false;
}
#endregion
}
PerformCompression
方法返回一个布尔值,根据请求中有效载荷的状态来决定请求是否可压缩。