Click here to Skip to main content
Click here to Skip to main content

Remoting Compression Channel Sink

By , 12 Nov 2008
 

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

aschmidt
United States United States
Member
Alexander Schmidt. I'm a software developer, who is working primarily with Microsoft technologies including Microsoft .NET. I'm also interested in optimization problems and software engineering in general. You can visit my blog at http://www.alexschmidt.net

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralSolution for Sink Problem.memberteligaurav3 Mar '10 - 19:17 
Hi Friends
 
After two days wandering(searching for error) and debugging
 
I find in this article code if you can see below line
 
requestHeaders[CommonHeaders.CompressionSupported] = true;
 
but actually CompressionSupported and CompressionEnabled fields are declared string
 
in
 
internal class CommonHeaders
{
#region Headers
/// <summary>Header to hold the compression state.</summary>
public const string CompressionEnabled = "cm_Enabled";
/// <summary>Header to hold the compression supported flag.</summary>
public const string CompressionSupported = "cm_Supported";
#endregion
}
 

class
 
so if you change it to
 
requestHeaders[CommonHeaders.CompressionSupported] = "cm_Supported";
 
and
 
requestHeaders[CommonHeaders.CompressionEnabled] = "cm_Enabled";
 
above code will work without errors.
 
Rgds
Gaurav Teli
 
Enjoy....!!!
GeneralCompression Sink Problemmemberteligaurav2 Mar '10 - 23:26 
I have used above code samples.
 
it was working
 
but not it gives me errors
 
like , i request something but calling server
 
call is going properly
 
but at time of return
 
// Retrieve the response from the server.
ServerProcessing processingResult = _next.ProcessMessage(sinkStack, requestMsg, requestHeaders,
requestStream, out responseMsg, out responseHeaders,
out responseStream);
 
here i get responseStream as null
 
that cause problem.
 
if you have some idea please help me.
 
Thank you.
GeneralCompression Sink suddenly stopes working [modified]memberteligaurav19 Jan '10 - 19:36 
I am using CompressionSinkProvider Suddenly it stopes working
 
I see if i Compress Data it is not calling Server with compressed Stream
 
.
.
.
.
.
if (_compressionThreshold > 0 &&
requestStream.Length > _compressionThreshold &&
!IsCompressionExempt(msg))
{
// Process the message and compress it.
 

 
requestStream = CompressHelper.Compress(requestStream);
 
// Send the compression flag to the server.
requestHeaders[CommonHeaders.CompressionEnabled] = true;
}
 
// Send the compression supported flag to the server.
requestHeaders[CommonHeaders.CompressionSupported] = true;
 
.
.
.
 
in above if my data is compressed and after that if i call
 

 
...
 
// Send the request to the server.
_next.ProcessMessage(
msg, requestHeaders, requestStream,
out responseHeaders, out responseStream);
 
it is not calling Server side
ProcessMessage method.
 

can any one tell me what could be the problem.
 

thank you.
 
modified on Wednesday, January 20, 2010 4:30 AM

GeneralI don't see diference using this compression and without it!memberAlexsandro_xpt9 Aug '09 - 8:40 
So, how can I measure a bytes transfer time for I can see the fast transfer between client and server?
 
I test 30MB between two machines in wireless network.
 
Code:
request.Data.Value = new byte[30912768];//[500000]; = ~30MB
GeneralRe: I don't see diference using this compression and without it!memberaschmidt4 Nov '09 - 11:59 
You certainly won't see any difference using such small data size. I'd recommend to increase the size to say, 100 MB and execute the test multiple times and measure the difference. In addition, many factors affecting the timings, such as network speed, latency, network equipment, any hardware compression etc.
GeneralRe: I don't see diference using this compression and without it!memberAlexsandro_xpt4 Nov '09 - 14:22 
Ok. Do you can tell me why .net remoting sometimes is very slow in internet environment when we working own Class mine(no-primitive class like String or Int32) ?
Note:
I am working with .NET Remoting Message Redirection Channel Sinks[^]
GeneralRe: I don't see diference using this compression and without it!memberaschmidt16 Nov '09 - 5:26 
Alexsandro, there are many factors affecting the performance and you probably better to ask author of that article if you're having issues with it. Also, check out the following article about improving serialization performance - http://msdn.microsoft.com/en-us/library/ms979193.aspx[^]

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 13 Nov 2008
Article Copyright 2008 by aschmidt
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid