Kevin Kallberg

The life and times of a software developer

NAVIGATION - SEARCH

Achieving Delayed Messaging in ServiceStack with RabbitMQ and EasyNetQ

DelayedMessageStackOfLove

Recently I was looking for a good, durable way to schedule web events for a project at work.  The project requires high throughput, so I needed a fast, scalable solution for persisting these events.  As fortune would have it, about a month before the project began, a new plugin was announced for RabbitMQ that supported delayed messaging.  A message queue seemed like it might be a good fit for the project needs, so I decided to take a look at it.

Picking the pieces

At work, we use (the fantastic) ServiceStack web service framework.  We’ve used RabbitMQ in the past, and the plugin for ServiceStack makes it very easy to publish and consume messages.  However, ServiceStack’s RabbitMQ plugin is very opinionated and does not leave a lot of room for customization without going down to the RabbitMQ.Client library itself.  I wanted something that would allow me to retain a lot of the simplicity that the ServiceStack RabbitMQ plugin offers but provide further points of configuration.

Enter, EasyNetQ

EasyNetQ_LogoEasyNetQ is an open source client API for RabbitMQ on .NET.  It has over 100,000 downloads on NuGet.org and over 1,200 commits across 70+ contributors on GitHub.  Not only does the health of the project look great, but EasyNetQ also comes with a feature that I was specifically looking for: scheduling messages.  EasyNetQ has long supported scheduled messages through the use of a companion Windows service.  However, as of late April, 2015, support for the new delayed exchange plugin for RabbitMQ was added.

Putting it all together

Now that the pieces have been assembled, it is time to tie them all together.  The project is simple.  It consists of only a pair of services with a single operation each, a pair of POCOs and the AppHost, which initializes ServiceStack and the other components.

The Message

With RabbitMQ, we can simply create a POCO to push into a message queue.  For our sample, our POCO contains just two DateTime properties.  The Queue attribute comes courtesy of EasyNetQ and allows us to specify the name of the queue and exchange that are created in RabbitMQ.

[Queue("DelayedMessageSampleQueue", ExchangeName = "DelayedMessageSampleExchange")]
public class DelayedMessage
{
    public DateTime PublishTime { get; set; }
    public DateTime SentTime { get; set; }
}

The Services

The project contains two services: one to publish messages and one to consume them.  The PublishService is a simple ServiceStack service that responds at /publish.  A single parameter, MillisecondsDelay, can be specified on either the query string or in the post body.  A new DelayedMessage will be created and the PublishTime set.  The service will then get a reference to EasyNetQ’s IBus interface from ServiceStack’s IoC container and use it to publish the message.  Note the use of the FuturePublish method.

[Route("/publish")]
public class PublishRequest : IReturnVoid
{
    public double MillisecondDelay { get; set; }
}

public class PublishService : Service
{
    public void Any(PublishRequest request)
    {
        var message = new DelayedMessage { PublishTime = DateTime.UtcNow };

        var bus = HostContext.Container.Resolve<IBus>();
        bus.FuturePublish(TimeSpan.FromMilliseconds(request.MillisecondDelay), message);
    }
}

The DelayedMessageService is responsible for consuming the message queue when the message delay is reached.  The service sets the SentTime property on the message and then writes a message to the Debug output.

public class DelayedMessageService : Service
{
    // This is the queue consumer for the DelayedMessage queue.
    public void Any(DelayedMessage message)
    {
        message.SentTime = DateTime.UtcNow;

        Debug.WriteLine("{0}New Message Recieved!{0}\tPublished: {1}{0}\tSent: {2}{0}", 
            Environment.NewLine, 
            message.PublishTime, 
            message.SentTime
        );
    }
}

The AppHost

ServiceStack’s AppHost is where initialization of the message queue takes place.  EasyNetQ is registered using its static RabbitHutch.CreateBus method.  Note the registration of the IScheduler interface with a DelayedExchangeScheduler.  This is where we tell EasyNetQ to engage the RabbitMQ delayed exchange plugin.  Next, we configure the message queue to deliver our delayed messages to the DelayedMessageService we looked at above.  To do so, we use the IBus interface’s Subscribe method.  We pass an empty string as the subscriptionId parameter (since we’re using the Queue attribute mentioned previously) and an anonymous method for the callback.  When a delayed message is ready to be sent it is dequeued and enters the Subscribe callback.  Here, we convert the POCO message to an IMessage<T> and pass it to ServiceStack's ServiceController.ExecuteMessage method which delivers it to our service.

public class AppHost : AppHostBase
{
    ... snip ...

    /// <summary>
    /// Application specific configuration
    /// This method should initialize any IoC resources utilized by your web service classes.
    /// </summary>
    /// <param name="container"></param>
    public override void Configure(Container container)
    {
        var connectionConfig = new ConnectionConfiguration
        {
            AMQPConnectionString = new Uri("amqp://localhost:5672/"),
            UserName = "guest",
            Password = "guest"
        };
        container.Register(RabbitHutch.CreateBus(connectionConfig,
            x => x.Register<IScheduler, DelayedExchangeScheduler>()));

        var bus = container.Resolve<IBus>();
        bus.Subscribe<DelayedMessage>(String.Empty, msg =>
        {
            var message = new ServiceStack.Messaging.Message<DelayedMessage>(msg);
            HostContext.ServiceController.ExecuteMessage(message);
        });

        this.RegisterService<PublishService>("/");
        this.RegisterService<DelayedMessageService>("/");
    }
}

We round out the AppHost by registering our two services.

Gotchas

There are a couple things to keep in mind when using this solution.

  • Requires RabbitMQ Server 3.4 or higher
  • Requires new delayed exchange plugin

Wrapping up

With this new plugin for RabbitMQ and the slick implementation offered by EasyNetQ, it is very straightforward to achieve delayed messaging in ServiceStack.  With just a little bit of configuration you tie right in to the existing infrastructure.  The sample code can be viewed / downloaded in its entirety on GitHub

Xamarin.iOS Error: “Failed to obtain information about the remote's profile files. Unable to check SDK status.”

icon7-152I had been running into this issue the past few days.  I was using Visual Studio 2013 Professional and an iMac running Mavericks as my build host on the network.  I was able to open Xamarin.iOS projects in Visual Studio, but as soon as I tried to execute it I would get this error.  Google suggested that I make sure that both the Windows and Mac machines had been updated and that the product versions match on both sides.  After checking and re-checking this I was about to give up and try to reinstall when I stumbled onto the answer.

In a post on the Xamarin forums, Brendan Zagaeski of Team Xamarin provided the information that would be the missing piece to my puzzle.  His comment was in response to a different issue that a user was experiencing, but the part in bold was the key bit of information I needed.

…please try updating the clocks on both your Windows and Mac computer to the current accurate internet time. Part of security algorithm for the server-client communication requires that the clocks on both computers have closely matching times.

As expected, my Windows machine was synced with Internet time server time.windows.com and the iMac was synced to time.apple.com.  For whatever reason, there was an 8 minute discrepancy between the times on the machines, which was apparently enough for the security algorithm to fail. 

imageIn order to prevent this issue from occurring again in the future I have set my Windows machine to synchronize with the same Internet time server as the iMac.  Once updated and the clocks on both machines were in sync I was able to update the SDK on my Windows machine, allowing me to once again execute Xamarin.iOS applications from Visual Studio.

 

Windows Phone 8 Emulator and Local Web API

The Windows Phone 8 Emulator differs from previous Windows Phone emulators in that it is a full fledged virtual machine running on Hyper-V.  This creates difficulty when attempting to test local web services utilizing ASP.NET Web API.  The Windows Phone client cannot simply reference localhost like it did in the past.  Instead, you must make the service available to the emulator as an independent device.  There are a few hoops to jump through to make this happen and here's how I achieved success.

Reorder network adapters on the Emulator

  1. Remove all network adapters from the Emulator in Hyper-V Manager
  2. Add network adapters back on the Emulator beginning with the Wireless adapter, then Wired Ethernet, then Internal Windows Phone
  3. At this point I was able to access internet resources like google.com and kevinkallberg.com from within the emulator.

Tell IIS Express to allow traffic to connect on an IP address in addition to localhost

  1. Stop the Web API site.
  2. Right-click and Exit IIS Express in the system tray.
  3. Open the folder %USERPROFILE%\Documents\IISExpress\config\
  4. Edit the file called applicationhost.config
  5. Locate the Site XML node under <configuration><system.applicationHost><sites> that matches your site.
  6. Copy the binding node similar to this one:  <binding protocol="http" bindingInformation="*:<port>:localhost" />
  7. Paste the binding node directly underneath the existing one and replace localhost with your current IP address.
  8. Add additional bindings if you wish for each of the host machine's network adapters.
  9. Restart the Web API site and ensure that you can browse to it on the host machine via localhost:<port> and <ipaddress>:<port>

Add an exception in the Windows firewall

  1. Create an inbound rule allowing traffic on the port that IIS Express is hosting the Web API service.  
  2. Allow this exception on Private and Domain network profiles, but not Public.

At this point, you should now be able to access the Web API site from IE Mobile on the Windows Phone Emulator by navigating to http://<ipaddress>:<port>.

Remember you can use the Pause/Break key to toggle keyboard functionality inside the Emulator.

Good luck!