Home     About mPrest     Solutions     Products     Articles & News     Jobs @ mPrest
 
Persistent .Net Events in Stateless Remoting Server
Introduction
Remoting is a new and fascinating technology, and can be used to easily build distributed applications. I assume you already know how to use it, and know how to use remotable events. If you don't understand something in this article / demo application - read Ingo Rammer book (see below).
Sample application demonstrates useful patterns for deploying server metadata on clients and declaring shim classes for clients to be deployed on the server side. Some of the ideas were taken from the Ingo Rammer's book (Advanced .NET Remoting, ISBN 1-59059-025-2), www.dotnetremoting.cc.
If you want to create serious distributed applications, you should be concerned about fault tolerance and scalability of your servers. In general, remoting provides tools to create fully stateless and scalable applications (single call or stateless singleton server-activated objects).
Concept of event and multiple receivers is widely used in .NET, and even is supported by .NET remoting (although I think they could do better :) ).

Demo application
We'll look at simple client/server applications. Server has one event and one method, and raises event when method is called. Server will be hosted in command line application (can be hosted in IIS too), and will be stateless singleton. Client will be command line application too, and will subscribe to event, call method, and wait to "enter" before exiting. I won't discuss client in this article at all, because it is standard .NET client.

Using Demo Application.
If you run server, then client A, then client B – client A receives notification caused by the server method call made by B. Now you can exit client B and server, and execute them again. Leave client A running. When you execute client B, client A will get notification again – despite restarting of the server. So our server persists subscribed clients and calls them back based on the persisted data.
All code in this acticle is taken from the demo application, which includes all layers of the distributed application. Database used in this application can be re-created using included script – DB.sql. URLs and connection strings are read from configuration files, so you can customize it if you need. Demo application just has too much code to be presented in the body of the article, so I extracted fragments to highlight obscure points. :)


Problem description
Problems begin when you want to merge those two concepts – stateless remotable server and .NET events. Why? If your server is stateless, you (by the rule of thumb) can't have any instance or static variables in your server object, right? If you do have such variables, their values will be lost when server is restarted (so where is your fault tolerance), and they won't be shared between different instances of the server running on different hosts (so where is your scalability).
Solution is obvious – just store all those variables in the persistent storage and read it again each time when you handle client's request. When you declare event, you declare delegate, and then declare event of the delegate's type.

public delegate void BroadcastedMessageHandler(string text);
public event BroadcastedMessageHandler NewBroadcastedMessage;

So if you want to declare abstract server class with event, it will look like this
public abstract class AbstractServer : MarshalByRefObject
{
  public abstract string Say(string what);
  public event BroadcastedMessageHandler NewBroadcastedMessage;
}
I hope you understand that declared class has one variable – NewBroadcastedMessage. Moreover, this variable is public, and will be freely modified by each += and -= statements in the client code. Even if we do know how to persist this variable – we need to know when it is changed first, right?


Intercepting subscribe / unsubscribe operations
Imagine this variable is of int type - what would we do? Declare it as property. So the syntax of the abstract class will be:
public abstract class AbstractServer : MarshalByRefObject
{
  public abstract string Say(string what);
  public abstract event BroadcastedMessageHandler NewBroadcastedMessage;
}
Note abstract in the event declaration. And implementation class :
public class Server : AbstractServer
{
  public override event BroadcastedMessageHandler NewBroadcastedMessage
  {
  get
    {
    }
  set
    {
    }
  }
}

But what will we return from get? And how will we distinguish between += and -= in set? Ok, it looks like this syntax isn't good for events. And folks in Microsoft thought the same way :) So they invented the property-like syntax for events:
public class Server : AbstractServer
{
  public override event BroadcastedMessageHandler NewBroadcastedMessage
  {
    add
    {
    }
    remove
    {
    }
  }
}

Like with regular property, when you declare and implement it, no storage is generated and it is your responsibility to store changes and retrieve value when needed. Tip: code of both add and remove methods can access passed parameter through value keyword, just like in set method of property. What you get in the value is the delegate passed by caller of the += or -=.

Remoted delegate persistence
What is remoted delegate? Let's look what's going on when client subscribes to event. Client creates new delegate and passes it to the server. Remoting de-serializes this delegate and passes it to our add method. We receive ObjRef of the delegate's target object (consider it network pointer) and serialize it. The result of the serialization can be stored like any other data:

BinaryFormatter fmt = new BinaryFormatter();
ObjRef oRef = RemotingServices.GetObjRefForProxy((MarshalByRefObject)value.Target);
MemoryStream ms = new MemoryStream(4096);
fmt.Serialize(ms,oRef);

Now we can call ms.GetBuffer() and receive serialized stream, which later can be safely stored somewhere. Actually we persist the remoted object, not the delegate itself. To persist delegate, we need to store its name too. In our sample, for each event we know, the name of the method which can be subscribed (it is always public method of the abstract class that defines event sink interface (shim class)), so we use that knowledge when we need to reconstruct the delegate. And how do we reconstruct the delegate? Let's assume we have DataSet and Table containing serialized ObjRef.
MemoryStream ms = new MemoryStream(ds.tb_sinks[i].ObjRefStream,false);
object refObj = fmt.Deserialize(ms);
ms.Close();
I'd bet you think if you serialized ObjRef, you will get ObjRef back when deserializing it? Nope. ObjRef is the network reference, so folks at Microsoft thought it is much more logical to automatically create transparent proxy to the remoted object which was referenced by serialized ObjRef. Do you think now you can use this transparent proxy ? Nope. When it is created , it does not initialize itself with type information, and will throw exception whenever you access something specific to your type. So? Just cast it back to the correct type.
AbstractBroadcastedMessageEventSink sink = (AbstractBroadcastedMessageEventSink)refObj;
Now you have transparent proxy of the remoted object. Given object and name of the method, you can create the delegate:
BroadcastedMessageHandler d =
(BroadcastedMessageHandler)Delegate.CreateDelegate(
typeof(BroadcastedMessageHandler),
"BroadcastedMessageHandler"
);

//Now you can simple call the delegate:
d(m_Text);

Bells and whistles
You have all required information, so you can stop reading now :) Still, I decided to wrap this functionality and create some helper class. You can read the code in the attached applications. I separated code specific to each event type, and generic event persistence logic. PersistentEventsHelper implements all functionality of adding, removing and calling all existing sinks. You need to pass to its methods also event name – any string, just be consistent to pass the same value for add, remove and invoke methods. This string helps to tell apart sinks of the different events – the same helper can work with different servers, and each server can have multiple events.

Calling clients in parallel
You should always check results when calling remoted object - else you can end up with thousands of objects in your storage, that causes exceptions every time you call them. You need to catch this exception, and remove the object that caused exception from the storage. Usually it is done just by calling delegate in the try/catch block. The problem with this approach is that you will be making synchronous calls - i.e. if you need to call 50 sinks you will call second only after first returned. In addition, if your clients are GUI based, and client's code that accesses GUI elements from event handler does not marshal to GUI thread asynchronously, and event is delivered to the same client which caused its raising, your program will deadlock. (See PRB: GUI application hangs when using non-[OneWay]-Events FAQ for details). So I prefer to make all calls from the server to clients asynchronously. How? Simple by defining class, whose instance is responsible to check invocation result:

InvocationResultChecker checker = new
InvocationResultChecker(ds.tb_sinks[i].ID);
Caller.BeginInvoke((MarshalByRefObject)refObj,
new AsyncCallback(checker.Callback),null);

Callback method calls EndInvoke and in case of any error, removes sink with the stored ID from persistent storage. Server defines class BroadcastMessageEventContext – one class for each event type. This class makes all castings and calls that are specific to event, so PersistentEventHelper is kept clean and generic.
class BroadcastMessageEventContext
{
  string  m_Text;   

  public BroadcastMessageEventContext(string text)
  {
  m_Text = text;
  }

  public void RemoteHandlerCaller(MarshalByRefObject TargetObject)
  {
AbstractBroadcastedMessageEventSink sink =
(AbstractBroadcastedMessageEventSink)TargetObject;
BroadcastedMessageHandler d = (BroadcastedMessageHandler)
Delegate.CreateDelegate(
typeof(BroadcastedMessageHandler),
sink,
//should be read from storage too,
//hardcoded here for simplicity
"BroadcastedMessageHandler"
);
d(m_Text);
  }
}


Conclusion
Now you know how to use .NET events in fault tolerant and scalable applications. Obviously this demo application isn't of production quality, and you should review code before using it in your real-world applications. Specially you should consider handling of remoting errors - current implementation forcefully unsubscribe the client, so it is possible for the client to run endlessly without receiving notifications and not knowing it. You should implement some keep-alive mechanisms when client will test if it is still subscribed. You should also think about retry mechanism - in the WAN deployment it is possible to get short-time connectivity problems, so probably it is worth to try deliver notification in a second or two. Enjoy ! :)

click here to return the articles page


 
© mPrest LTD.