Ok, I am still struggling with the use of RabbitMQ.
My first service is MyAppAPI and it communicates with a client through HTTP calls. I have for example:
public class CalculationService : Service
{
public CalculationResponse Post(CreateCalculation request)
{
// Persist calculation input in the database...
calculationRepository.Add(request)
// Publish calculation to queue to be picked up by solver...
using (var mqClient = mqServer.CreateMessageQueueClient())
{
mqclient.Publish(new Calculation(request));
}
}
}
My second service is MyAppSolver and it is deployed as a standalone Windows Service, its aim is to wait until Calculations are put on a queue using RabbitMQ message broker, pick them up from there, process them and then publish an answer on another queue.
public class CalculationSolverService : Service
{
public CalculationResult Calculate(Calculation calculation)
{
return new CalculationResult() { Id = "result" };
}
}
So on this service I have, following your advice, registered my Calculation object:
And setting up a breakpoint, I can see the request being processed and “receiving” the message. The first time I just used ExecuteMessage and as there was no answer I saw the message going to the .dlq.
So I am getting there but how can I “elegantly” leverage RabbitMQ integration in ServiceStack: my solver Service does not need any HTTP communication, I would like the scenario https://docs.servicestack.net/rabbit-mq#process-servicestack-mq-services-without-a-http-host but I am failing to implement it “nicely” starting from the selfthost-netfx template and comparing it to the MqServerIntroTest.
DTOs should just be data structures, i.e. they shouldn’t have constructors as that implies logic in the DTO which can’t be translated into DTOs in different languages and DTOs without parameterless constructors can cause issues with different serializers which need to rehydrate them from a serialized payload, basically don’t use constructors with params in DTOs.
The other issue I see with this example is that you have both a Calculation service and you’ve also registered a mqServer.RegisterHandler<Calculation>() MQ handler, it should be one or the other and only registered on the AppHost that handles Calculation Messages/Request DTOs.
The response to your Calculation Service is CalculationResult which if successful will be sent to the .inq MQ, so you could register a handler to handle the CalculationResult response or create another Service that handles it which is where the Response will be processed.
You also say messages are going into your DLQ so there is some Exception which is causing it and you should check the DLQ payload to see if you can identify it. Error details are typically injected in the ResponseStatus property of your Response DTO so as a convention you should include it in your Response DTOs:
public class CalculationResult
{
//....
public ResponseStatus ResponseStatus { get; set; }
}
Point 1: Granted, I did some editing for my this post and used a bad shortcut
Point 2: my CalculationService service is in the first service, the Calculation is the DTO I am using (probably a bad naming convention) for my 2nd service CalculationSolverService. My initial understanding was in the 2nd service, I would register the DTO mqServer.RegisterHandler<Calculation>() and in the first service I will use a POCO Calculation to publish into the Queues created automatically.
Now on this 2nd Service side, the CalculationSolverService, this service does not get called through HTTP. It should just sit there, check on the queue of messages and process the calculations. So how do I trigger any of its functions (like Calculate which was taking the Calculation DTO object as request)? I tried registering the all service and publishing on that queue but again with no luck.
There’s no other configuration, as long as both message producer and message handler are both registered to use the same Rabbit MQ Server, publishing a Caclulation Request DTO will be sent to one of the Services that has a registered handler for that Request DTO (i.e. if you have more than one registered handler like multiple load-balanced App Servers it will only be handled by one of them). If the message is sent to the .dlq then you should investigate what that Exception was. There is an overload on RegisterHandler that lets you specify an Exception handler where you can debug and inspect the Exception that was thrown whilst trying to process that message.
Ok I have now a message error that Calculation cannot be resolved (Unable to resolve service 'Calculation')
Host 1 is the publisher and publish a Calculation POCO object.
public class JobManagementService : Service
{
public CalculationResponse Post(CreateCalculation request)
{
// Persist calculation input in the database...
calculationRepository.Add(request)
// Publish calculation to queue to be picked up by solver...
calculation = new Calculation();
calculation.prop1 = request.prop1;
.....
// publish calculation
base.PublishMessage(Calculation);
}
}
And its RabbitMQ configuration for now register NO HANDLER
private void ConfigureRabbitMqServer(Container container, string connString)
{
container.Register<IMessageService>(c => new RabbitMqServer(connString));
var mqServer = container.Resolve<IMessageService>();
mqServer.Start();
}
Host 2 contains the solver service that should receive this POCO object:
public class CalculationSolverService : Service
{
public CalculationResult Calculate(Calculation calculation)
{
return new CalculationResult() { Id = "result" };
}
}
and Host 2 has also the following configuration:
private void ConfigureRabbitMqServer(Container container, string connString)
{
container.Register<IMessageService>(c => new RabbitMqServer(connString));
var mqServer = container.Resolve<IMessageService>();
mqServer.RegisterHandler<Calculation>(ExecuteMessage);
mqServer.Start();
}
And still all my messages go to the .dlq unabled to resolve the service.
public object Any(Calculation request) => new CalculationResult { Id = "result" };
All ServiceStack Service methods needs to either be the HTTP Verb it’s handling, e.g. Get,Post,Put.Delete,Patch,etc or Any() to indicate it can handle a request sent on any verb.
MQ and SOAP requests are treated as Post so the method name should either be Post() or Any().
Ok getting there , this works fine CalculationService Host -> CalculationSolverService Host, the CalculationServerService picks up well the Calculation request and sends a CalculationResult message back.
Now for the moment I just want my CalculationService host to pick up that message but if I create a
public CalculationResponse Any(CalculationResult request)
{
....
}
then the message gets passed to the CalculationResponse.inq as per the message workflow.
Now following the message workflow I tried:
public object Any(CalculationResult request)
{
// do some stuff with the request
....
return null;
}
That does stop the message in the CalculationResult.outq but is it a “legit” way of defining a Service method? If not how can I stop the message “elegantly”?
This is part of the messaging workflow where messages with no/null responses notify the non durable mx.servicestack.topic that subscribers can listed on to be notified when a message has finished processing. So returning null is one way to prevent a message from being published to the Response Type .inq.
An alternative is to specify an opt-in whitelist of only the Response Types you want published to the .outq, e.g:
new RabbitMqServer {
PublishResponsesWhitelist = new[] { typeof(CalculationResult).GetOperationName() }
}
This will limit it so only CalculationResult responses are published.