Introduction
One of the problems with Remoting in real world is that sometimes it is required to transfer a large chunk of data between a client and a server. There is a big overhead with the default binary serializer, which is very chatty. The result is, Remoting errors associated with large requests and poor performance. There is a global solution, which allows compressing the Remoting requests and responses, thus alleviating the problem of exceeding the Remoting maximum request size. This is an in-depth look at a Remoting extendable architecture, which provides detailed information of the implementation of compression.
Remoting
A possible design approach related to compression can be built around a Remoting extendable architecture and the customization of the channel. Channels send each message along a chain of channel sink objects prior to sending, or after receiving, a message. By default, a sink chain contains sinks required for the basic functionality of the channel, such as proxy, formatter, and transport. The channel sink chain processes a message that is sent to or from an application domain, and this process can be augmented by inserting a custom processing sink, in our case, a custom compression sink, and subsequent processing will use the message that is returned to the system after processing. The following illustration shows the structure of a channel sink chain:
Both the server and the client channels can be configured to use compression programmatically, or by using a configuration file. The client side compression sink is implemented using two classes where one is a provider, CompressionClientChannelSinkProvider
, and the other a sink, CompressionClientChannelSink
. For the server side, the sink is implemented using two classes, where one is a provider, CompressionServerChannelSinkProvider
, and the other a sink, CompressionServerChannelSink
. Next, we will review a sample code with the custom compression sink, which represents a typical application designed using layered architecture, where the services layer is responsible for communication with other layers using a request/response design pattern.
Sample
We have created a sample application which utilizes the compression channel sinks. Defined below is a sample request object derived from a generic ServiceBase<t>
class:
[Serializable]
public class ConcreteTypeRequest : ServiceBase<concretetype>
{
private int _userId;
public int UserId
{
get { return _userId; }
set { _userId = value; }
}
public ConcreteTypeRequest()
{}
}
Next, a server configuration file and a client configuration file should contain the settings for the custom compressing sink on the server and the client side, respectively. Below is an example of the config file for stackable providers using the Remoting extendable framework. The most important thing is the order in which a provider and a default formatter is specified within the serverProvider
section. Also, it provides a compression threshold parameter, which allows specifying the threshold in bytes:
Server configuration:
<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>
Client 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>
By default, the compression is enabled for all requests and responses within an AppDomain. If a request size in bytes is under the threshold, the request is transferred without being compressed. If it is greater than the threshold which, in our case, is equal to 500000 bytes, it is compressed before being transferred to the server. If we want to opt out, the NonCompressible
attribute should be specified on the class, as follows:
[Serializable]
[NonCompressible]
public class ConcreteTypeRequest : ServiceBase<concretetype>
{
private int _userId;
public int UserId
{
get { return _userId; }
set { _userId = value; }
}
public ConcreteTypeRequest()
{}
}
Moreover, the NonCompressible
attribute is unconditional, thus permanently disabling the compression for this type of request. This might be useful for some scenarios in the enterprise, when, for example, a request's payload is already compressed. If you want the compression to be conditionally applied to certain instances, a request may implement an ICompressible
interface as follows:
[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
}
The PerformCompression
method returns a boolean value, which determines if the request is compressible based on the payload's state within a request.
And finally, attached is a sample application which implements the compression channel sink, a server, and a client application. It is developed for Microsoft Visual Studio 2005, and contains a console application for testing purposes.
Conclusion
To sum up, we looked at the extensibility of the Remoting architecture by implementing a custom compression sink, which solves the problem of performance encountered when large requests and responses are sent over the wire. We implemented the configurable sink when a request or response can be opted out from the compression unconditionally, or based on the conditions determined at runtime. We reviewed the settings for the configuration files which are required to plug into the Remoting infrastructure, and how to configure a threshold for compression. Finally, we looked at a sample application which demonstrates a working prototype of the compressing sink.
History
- November, 12, 2008 - Initial revision.