query event publisher if a particular subscriber is still subscribed

S

Stephan Steiner

Hi

I have a remoting based even publish and subscribe mechanism in place which
works quite fine. However, due to the distributed architecture, it can be
that the publisher might at some point be inaccessible - and if it comes
back up, the subscriber has to re-subscribe (similarly, the publisher will
remove the appropriate event handler if the event cannot be raised anymore
at some point - in order to do this, I'm iterating over the registered
subscribers by calling mydelegate.GetInvocationList()).

So far, I only had one subscriber, so when I was polling the publisher, I
simply checked a boolean variable which I set to true when the subscriber
made the subscription.

However, in preparing for multiple subscribers, I can no longer use a
boolean - so I'm keeping a data table instead. However, now I need some way
for a subscriber to ask the publisher if he is still registered to receive
event notifications... I tried using the hashcode of the delegate, but it
has a different value on the publisher and subscriber so that's out. While I
can get the IP address of the subscriber, that also doesn't fly because
there's a NAT on the way (for the publisher, every subscriber but a
subscriber running on the local machine has the same IP).

I figure I need to be able to add some kind of unique identifier when
subscribing to the event

class.Event += eventHandler;

However, since I cannot inherit from MulticastDelegate I'm also stuck that
way.

So, is there another way to get some kind of unique identifier across so
that a subscriber can at some point query the publisher "am I still
registered for event notification" (preferably something more simplistic
than writing my own sinks on server and client application to inject a
unique identifier into the connection on the client side so that it can be
extracted again on the server side):

Regards
Stephan
 
N

Nicholas Paldino [.NET/C# MVP]

Stephan,

Why not just add an extra event to your publisher which will effectively
act as a ping? If the call to the client responds, then you know it is
still subscribed, if there is an exception, then there isn't.

I think that events in distributed environment are tenuous, at best.
Instead, you should have an event interface which your subscribers will
implement (using MarshalByRefObject as the base class of course).

Then, when your subscribers call the method to subscribe, and you store
the interface pointer, you should be able to use the remoting infrastructure
(MarshalByRefObject, ILease, ISponsor, etc, etc) to determine the status of
the connection to the object referred to by the proxy).
 
S

Stephan Steiner

Nicholas

I may have not provided enough implementation details. The - the event
pushes data from the publisher to the subscriber, but the ping is executed
by the subscriber to tell the publisher that it's still alive. From a
publisher standpoint, I know when a subscriber is no longer active - it's
when I try calling the event and get an exception (at that point I
unregister the handler so that the client no longer gets notifications.. and
that's why I have the ping.. the subscriber keeps asking if he's still
subscribed, and if the publisher says no, the subscriber re-subscribes).

Here's my definition of the code common to publisher and subscriber:

public delegate void logEventHandler(string line);

public interface RemoteSupportServerLogInterface
{
bool ping();
event logEventHandler logEvent;
}

public abstract class AbstractEventSink : MarshalByRefObject
{
public override object InitializeLifetimeService()
{
return null;
}
public void logEventCallback(string line)
{
internalLogEventCallback(line);
}
public abstract void internalLogEventCallback(string line);
}

On the publisher side (it's at the same time the server side.. it has a
bunch of traditional remoting interfaces it exposes, plus it consumes
remoting interfaces of other remote machines), it looks like this:

public void fireLogEvent(string message)
{
if (logEventHandler != null)
{
foreach (logEventHandler handler in
logEventHandler.GetInvocationList()) // iterate over all registered handlers
one by one
{
try
{
handler(message);
}
catch (Exception e) // we have a problem, assume the
controller is no longer connected
{ // catch code here}
}
}
}

public event logEventHandler logEventHandler;

public event logEventHandler logEvent
{
add
{
logEventHandler += value;
}
remove
{
logEventHandler -= value;
}
}

The class that contains that code implements both MarshalByRefObject, and
the interface defined above.

The subscriber itself has this class handling the events:

public delegate void logEventReceivedCallback(string line);
public class ServerConnector: AbstractEventSink
{
public event logEventReceivedCallback LogEventReceived;

public ServerConnector()
{
}
public override void internalLogEventCallback(string line)
{
if (LogEventReceived != null)
LogEventReceived(line);
}
}

And the class that sets up remoting creates an instance of ServerConnector,
and assigns the logEventCallback as event handler for the logEvent defined
in the remoting interface (comm is the pointer to the remoting interface).

logHandler = new logEventHandler(conn.logEventCallback);
comm.logEvent += logHandler;

(note that all the above is stripped down.. there's more (unrelated to the
issue at hand) and there's error handling that I left out for brevity.

So, I think I already have in place the event interface - but I'm unsure on
how the subscriber would figure out if it's still subscribed (which may or
may not be the same thing as connected.. I've seen it plenty of times that
ping still worked, whereas the event handler was no longer registered -
that's the main reason I came up with the two way communication... events
from publisher to subscriber, and a regular remoting method from subscriber
to publisher to query the event subscription state). Also, if the publisher
unexpectedly goes down, then keeping on pinging will ensure that as soon as
the server is back up, the even registration can be done again and that
future events will be received.

I also need to ensure that at any given point, the subscriber has only
subscribed once or it ends up getting all the messages multiple times which
is very undesirable.

Regards
Stephan
 
S

Stephan Steiner

I forgot about something..

You mentioned ILease, ISponsor and the likes. The you might have noted that
I'm overriding InitializeLifetimeService and returning null - meaning I
don't use the whole lifetime management. This is because for this particilar
interface (plus some others.. anything that needs a shared state), I need a
singleton object that lives on forever - so clients come and go but the
object remains there doing its thing (I have one singleton class that
contains fireLogEvent, and I want one call to fireLogEvent to push the
message out to every subscribed client at any given time).

That may complicate things because now the client cannot use the lifetime
management options to figure out if the lease on the remoting interface is
still active and is restricted to having to ask the publisher.

Regards
Stephan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top