Update docs on Messaging wiki page

Here’s something that bit me, maybe you can add a note to the docs on the wiki (https://github.com/ServiceStack/ServiceStack/wiki/Messaging)

The example looks like this:

public class AppHost : AppSelfHostBase
{
    public override void Configure(Container container)
    {
        //Register to use a Rabbit MQ Server
        container.Register<IMessageService>(c => new RabbitMqServer());

        var mqServer = container.Resolve<IMessageService>();

	//Register message handlers here...
        mqServer.RegisterHandler<HandleExampleMessageRequest>(ServiceController.ExecuteMessage);
		
        //Start the Rabbit MQ Server listening for incoming MQ Requests
        mqServer.Start();
    }
}

However, if your message handler calls a service, which publishes a new message, you could get a NullReferenceException on the first message or two (depending how busy the queue is).

public class ExampleService : Service
{
    public void Post(HandleExampleMessageRequest request)
	{
	    //Do stuff here
		
		//This throws NRE on the first call if the MQ service is started in AppHost.Configure
		PublishMessage(new DoNextStep
		{
		    Id = request.Id
		});
	}
}

Stack trace

System.NullReferenceException: Object reference not set to an instance of an object.
   at ServiceStack.ServiceStackHost.GetMessageProducer(IRequest req)
   at ServiceStack.Service.get_MessageProducer()
   at ServiceStack.Service.PublishMessage[T](T message)
   at ServiceInterface.ExampleService.Post(HandleExampleMessageRequest request) in ExampleService.cs:line 10
   at lambda_method(Closure , Object , Object )
   at ServiceStack.Host.ServiceExec`1.<>c__DisplayClass6.<CreateExecFn>b__5(Object service, Object request)
   at ServiceStack.Host.ServiceRunner`1.Execute(IRequest request, Object instance, TRequest requestDto)

This is because the IMessageFactory (which is automatically registered in ServiceStackHost.OnAfterInit) is not available until the AppHost has completed initializing.

To avoid this problem, the MQ service should be started in an AfterInitCallback.

public class AppHost : AppSelfHostBase
{
    public AppHost()
        : base("My Services", new[] { typeof(HandleExampleMessageRequest).Assembly })
    {
        AfterInitCallbacks.Add(StartMqServer);
    }
    
    private void StartMqServer(IAppHost appHost)
    {
        //Start MQ consumers
        appHost.GetContainer().Resolve<IMessageService>().Start();
    }
}

Looks like an odd race condition (not experienced myself) where the Code on the main thread doesn’t complete before the IO Request to fetch a message from the MQ completes. Are you doing any blocking calls after mqServer.Start() that’s slowing AppHost.Configure() down?

You could register the MessageFactory yourself which IMO is preferable:

container.Register(c => mqServer.MessageFactory);

Otherwise it’s easier if you just queue up starting in AppHost.Configure(), e.g:

AfterInitCallbacks.Add(host => mqServer.Start());

But I’ve just added a fallback in GetMessageProducer() so it resolves IMessageService if IMessageFactory wasn’t registered yet.

I tried moving the mqServer.Start() call right to the end of the Configure method, but still had the problem.

I see that the auto-registration of the IMessageFactory happens after plugins are loaded.

I have a few plugins that look through my service model assemblies for all types implementing a certain interface, then do something for each type.

Loading these plugins can take a few seconds, which is long enough for the MQ consumer threads to start processing messages.

I used the AfterInitCallbacks solution in the end, but the new fallback solves the problem either way.

1 Like