Communication between MDrivenServers

Background

If you build a whole (micro) service universe, you will soon notice a need for events triggered from one system sent to another.

This can - and is - often handled as Rest-api's on two systems that call each other. While this strategy may get the job done, you have an issue with the fact that the two systems must know about each other - and this quickly turns into a spaghetti-tangled web of services that cannot change without breaking something.

A much better approach is to have the more generic of the two systems expose a generic callback - an event.

Events

SignalR

Such events are best implemented by an open socket that can be used by the generic system to signal events even if it knows nothing about the listener, which in turn may cause action in the caller.

In .net SignalR is implementing this infrastructure.

In MDrivenServer, we use SignalR to enable internet signaling and event sending to subscribers

Turn on websocket in IIS

RealtimeSubscribeToEvent

Create an action column on a serverside ViewModel named RealtimeSubscribeToEvent. You will need these columns:

  • realtimehub = the MDrivenServer-url to the system to subscribe to - leave blank for THIS system
  • eventguid = a unique id (as a string) that makes this event unique on instance level - you will want to share this eventguid with the event sender - because that will make your event cookie come back to your system in the ReceiveEvent(theEventGuid, theEventCookie) callback
  • eventcookie = what you want the server to send back on event - suggested value 'Viewmodel=SomeVM&RootId='+self.externalid

You can call RealtimeSubscribeToEvent multiple times - only the latest eventCookie for a server/eventguid combination will be kept. If you send in a blank eventCookie or an eventCookie=="endsubscription" the subscription will be removed from the server.

RealtimeTriggerEvent

Create an action column on a serverside ViewModel named RealtimeTriggerEvent. You will need this column:

  • eventguid = a unique id (as a string) that makes this event unique on the instance level - you want to share this eventguid with the event subscriber. No matter how many subscribers you have, they will receive their event cookie.
  • eventdata = an optional string the sender can fill - not currently used by the receiver.
  • eventRemoveAfterSend:bool = optional, default false. If true, the client will only be notified this one time.

The Receiving MDrivenServer

The receiving MDrivenServer will use the event cookie that comes back when the event is triggered in the ReceiveEvent(theEventGuid, theEventCookie) to create a SysAsyncTicket with the ViewModel and RootId taken from the cookie.

If you have a non-MDrivenServer as an event publisher - and you want to hook up the MDrivenServer as an event listener - then the event publishing system must allow for the following SignalR API to be called with:

method SubscribeToEvent(string eventguid,string clientcookie );
event ReceiveEvent(string eventguid, string ClientCookie, string eventData);

The Event Sending MDrivenServer

The event publishing MDrivenServer will, upon executing the RealTimeTrigger serverside vm action, use the eventguid and call the ReceiveEvent that will filter out the connected listening clients on eventguid - and send the clientcookie and eventData.

If you build another non-MDrivenServer system that wants to consume events from an MDrivenServer, you must expect to receive this event on your connection:

event ReceiveEvent(string eventguid, string ClientCookie, string eventData);

In this, you must parse the ClientCookie you sent in on SubscribeToEvent and act as you see fit.

Security

The events are open and not authenticated. Do not send sensitive data over events. Use events to trigger a just-in-time RestGet that is protected by AccessGroups.

Usecases

Usecase #1: The Multisender

Suppose you have an IOT device collecting movement in an area. You want 250 other services to react to this detection urgently, but you want to avoid having 250 services polling the IOT device continuously because the device is not on a fast server and the network connection is slow. A much better solution is to let the IOT device signal when detection is done. A naïve implementation lets the 250 services register in the IOT device and has the IOT device roll through the list to try to connect to the clients one by one. This may fail because some of the clients are behind firewalls, and some have been decommissioned recently.

Better solution: We let the IOT device expose an eventguid that is "MovementDetected". All the 250 services call IOTDevice.SubscribeToEvent("MovementDetected", clientcookie). All the 250 listeners have their own way of reacting - all give different client cookies. On detection of movement, all the 250 get notified swiftly on an already open socket. Depending on the need, the client may follow up with an authenticated RestCall to the Device to get further details important to its function.

Usecase #2: The One-to-one Sender

Suppose you have a generic service converting open-document-standard-files (.odt) to pdfs. Your other 250 systems want to use this capability to produce pdfs, but they need continuous feedback to show to an eager user waiting to see the result. We want to avoid polling the pdf generator too much with "are you done yet" questions - at the same time, we do not want our user to wait 5 seconds too long.

We create a RestPost API on the Pdf-service that receives the raw data but also a guid-string that acts as our eventguid. Thereafter, we call RealtimeSubscribeToEvent giving the URL to the Pdf-service, the guid-string, and an event cookie that we define as 'Viewmodel=DoThisWhenSomethingImportantHappendWithMyPDF&RootId='+self.externalid. We can then forget about this job and wait for the DoThisWhenSomethingImportantHappendWithMyPDF serverside ViewModel to be executed. This will probably pull the finished Pdf from the service and signal the user with an update of a real-time tagged attribute that updates immediately if someone is watching it. No network use during the wait and no unnecessary waiting done.

This page was edited 158 days ago on 06/17/2024. What links here