Simple, Custom Remote Events in Sitecore

Every so often, it may be useful to trigger code execution on a remote server from a local server. Thankfully, Sitecore has a concept for this- Events. There are two distinct pieces that make up “Events”: Remote Events and local events. The community docs on GitHub has a great overview of the differences between local and Remote events. At face value, the two event concepts are unrelated, however, they are often linked together. I was able to find one example detailing how Remote Events and local events can be utilized to trigger code from one server to execute code on all remote servers. In a recent deep-dive, I found that Sitecore has a few built-in features to make this slightly easier than the existing post. The technique is outlined below.

Create Remote Event Object Class to Pass Data Between Servers

[DataContract]
public class LogMessageRemoteEvent : IHasEventName
{
public LogMessageRemoteEvent(string instanceName, string eventName)
{
InstanceName = instanceName;
EventName = eventName;
}
// This is the custom data you will be passing between servers
// Any serializable data type (int, string, bool, simple class) can be used
[DataMember]
public string InstanceName { get; protected set; }
// This is implemented from IHasEventName and bridges the gap between the remote and local event
[DataMember]
public string EventName { get; protected set; }
}

There are two important pieces of this class:

  1. It implements IHasEventName. This will make it easier to execute the local code later on.
  2. Notice the DataContract and DataMember attributes. An instance of this class will ultimately be serialized into the EventQueue database table and thus the class must be serializable. This class should contain the data that you wish to pass between servers. In this example, I will be sending the InstanceName of the server that triggered the event.
  3. Note: This is only an example. The InstanceName is already included with any Remote Event added to the EventQueue. Use your imagination/requirements to determine what else could be passed between servers.

Subscribe to Remote Event and Setup Local Event Trigger

public class LogMessageRemoteMap
{
public void Initialize(PipelineArgs args)
{
EventManager.Subscribe<LogMessageRemoteEvent>(new Action<LogMessageRemoteEvent>(OnGenericRemoteEvent<LogMessageRemoteEvent>));
}
private static void OnGenericRemoteEvent<TEvent>(TEvent @event) where TEvent : IHasEventName
{
RemoteEventArgs<TEvent> remoteEventArgs = new RemoteEventArgs<TEvent>(@event);
Event.RaiseEvent(@event.EventName, (IPassNativeEventArgs) remoteEventArgs);
}
}

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<initialize>
<processor type="MyNamespace.LogMessageRemoteMap, MyDll" method="Initialize" />
</initialize>
</pipelines>
</sitecore>
</configuration>

view raw
LogMessageRemote.xml
hosted with ❤ by GitHub

This code accomplishes two things:

  1. Upon site initialization it instructs Sitecore to add a remote event subscriber to watch for an object of type LogMessageRemoteEvent added to the EventQueue
  2. Links the execution of the Remote Event with the raising of a local event. This executes the local EventName and runs all handlers associated with it on the remote server (the server that the EventQueue is executing from at the time)

Specify the Code To Execute on Remote Servers

public class LogMessageRemote
{
public void LogInstanceName(object sender, EventArgs args)
{
var logMessageArgs = args as RemoteEventArgs<LogMessageRemoteEvent>;
if (logMessageArgs == null)
throw new InvalidOperationException("Unexpected event args: {0}".FormatWith((object) args.GetType().FullName));
Log.Info($"Triggered from {logMessageArgs.InstanceName}.", (object) this);
}
}

view raw
LogMessageRemote.cs
hosted with ❤ by GitHub

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<events>
<event name="logMessage:remote">
<handler type="MyNamespace.LogMessageRemote, MyDll" method="LogInstanceName" />
</event>
</events>
</sitecore>
</configuration>

view raw
LogMessageLocal.xml
hosted with ❤ by GitHub

This code reads the InstanceName of the calling server and logs it to the local logs of the remote server. The InstanceName is passed between servers via the EventQueue.

Trigger the Remote Event

var remoteEvent = new LogMessageRemoteEvent(Sitecore.Configuration.Settings.InstanceName, "logMessage:remote");
Sitecore.Eventing.EventManager.QueueEvent<LogMessageRemoteEvent>(remoteEvent);

view raw
QueueRemoteEvent.cs
hosted with ❤ by GitHub

This code can be executed anywhere within Sitecore. It adds an object to the EventQueue that will be picked up on each server individually as they process the EventQueue. By default, this is every 2 seconds.

Notice the same serialized event object (LogMessageRemoteEvent) could be passed to different local events because the local eventName is a parameter. Continuing this example, instead of logging the InstanceName of the calling server on each remote server, you could have each remote server phone-home to the calling server with some type of health status.

One thing that may not be obvious with this type of an implementation is that it does not require any special config transformations. All servers that use the same EventQueue (the same cluster) all use the same config files. The EventQueue orchestrates which servers run the code and which do not via the runLocal and runGlobal flags. In this example, all servers EXCEPT the calling server will log the message.

The brains of this post were delicately pulled from Sitecore.Eventing.Remote.RemoteEventMap. This post would be been a few lines shorter had the method Sitecore.Eventing.Remote.RemoteEventMap :: OnGenericRemoteEvent been public instead of private :(.

3 thoughts on “Simple, Custom Remote Events in Sitecore

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.