How to log the response message in a Registered Handler - ServiceStack RabbitMQ - servicestack

Given this snippet of code:
//DirectApi
mqServer.RegisterHandler<LeadInformationInfo>(m =>
{
repository.SaveMessage(m as Message);
LeadInformationInfoResponse response = new LeadInformationInfoResponse();
try
{
var client = new JsonServiceClient(settingsFactory.GetMasterSetting("ProcessorApi:baseUri"));
response = client.Post(m.GetBody());
}
catch (WebServiceException webServiceException)
{
_log.Error("RegisterHandler<LeadInformationInfo>", webServiceException);
response = ((LeadInformationInfoResponse) webServiceException.ResponseDto);
response.CorrelationId = m.Id;
}
// Log response message here
return response;
}, 1);
I've gone to great lengths to make sure that a correlationId based off the original message Id property is propagated through the life of this message and any child messages spawned from this action. How do I get a handle on the response message so that I may log it in the handler? I only have access to the ResponseDto and not the message.
One of the reasons for this request is that the message queue client does not have access to the database, only the process that has the handler registered does. Hope that explains the situation better.
Just to clarify, this question is about persisting a MQ Response Message in the handler, the correlation Id is something that all messages in 1 request/response workflow will share. I'm also using ServiceStack ORMlite to persist the Message object, so querying this table by ID for troubleshooting is paramount.
Thank you,
Stephen

You're calling a WebService from within your MQ Handler:
var client = new JsonServiceClient(...);
response = client.Post(m.GetBody());
So there is no MQ Response which is only available in MQ Services. Although the WebService will return the response for the request that's sent so you can either use the CorrelationId on the MQ Request, otherwise you can have your Response DTO implement an interface like IHasCorrelationId and get it that way, e.g:
var correlationResponse = response as IHasCorrelationId;
if (correlationResponse != null)
{
var correlationId = correlationResponse.CorrelationId;
}
Create your own Instance of Message
As the Message<T> class is just a POCO if you wanted to create your own you can intialize your own instance:
var mqResponse = new Message<Response>(response);
If you only had the runtime late-bound type info, you can create one with:
var mqResponse = MessageFactory.Create(response);
Use RabbitMQ Message Filters
If you just wanted to log incoming and outgoing messages you can use the RabbitMQ Message Filters, e.g:
var mqServer = new RabbitMqServer("localhost")
{
PublishMessageFilter = (queueName, properties, msg) => {
properties.AppId = "app:{0}".Fmt(queueName);
},
GetMessageFilter = (queueName, basicMsg) => {
var props = basicMsg.BasicProperties;
receivedMsgType = props.Type; //automatically added by RabbitMqProducer
receivedMsgApp = props.AppId;
}
};

Related

Azure service bus - not seeing messages

I created a simple Azure Service bus (Queue) and a client that is sending message to service bus. Using below code to send message:
using Microsoft.Azure.ServiceBus;
using Microsoft.Extensions.Configuration;
public async Task SendMessageAsync<T>(T message, string queueName)
{
try
{
var queueClient = new QueueClient(_config.GetConnectionString("AzureServiceBus"), queueName);
string messageBody = JsonSerializer.Serialize(message);
var byteMessage = new Message(Encoding.UTF8.GetBytes(messageBody));
queueClient.SendAsync(byteMessage);
Console.WriteLine((message as Employee).FirstName);
}
catch (Exception ex)
{
var c = ex;
}
}
Sending message using:
using SenderApp;
Console.WriteLine("Hello, World!");
QueueService service = new QueueService();
for (int i = 0; i < 100; i++)
{
Employee e = new Employee();
e.FirstName = "1 " + i.ToString();
e.LastName = "2 " + i.ToString();
service.SendMessageAsync<Employee>(e, "employeequeue");
}
When I try to see active messages, There is nothing in the queue:
However I do see some traffic. But the number of message I sent (over 100) is not equal to number of incoming request show (62) at the bottom of the image. I am not sure what is happening to my messages? This defeats the purpose of the queue.
Please guide me why I am not seeing any messages. What is the best way to handle this ?
I am using following nuget packages:
<PackageReference Include="Microsoft.Azure.ServiceBus" Version="5.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
A message sent to an Azure Service Bus queue will be delivered to the queue unless operation is failing. In that case, an exception will be thrown. Check the following:
Exception handling doesn't swollow exceptions
Await asynchronous send operations to ensure messages are dispatched
Namespace/queue used for sending is what you use to receive
There are no competing consumers, actively receiving messages from the queue.
Validate TCP ports needed for AMQP are not blocked. If those ports are blocked, you could configure your client to use WebSockets.
So I still dont know what caused this issue. But I realized Microsoft.Azure.ServiceBus package was deprecated and later I started using Azure.Messaging.ServiceBus package to connect to service bus and things started to work.
I used following code to send message to queue:
string connectionString = "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=f3f+qMYTyVwE18YNl5J6ygJFi30v6J/Smph5HZvyQyE=";
string queueName = "employeequeue";
// since ServiceBusClient implements IAsyncDisposable we create it with "await using"
await using var client = new ServiceBusClient(connectionString);
// create the sender
ServiceBusSender sender = client.CreateSender(queueName);
// create a message that we can send. UTF-8 encoding is used when providing a string.
ServiceBusMessage message = new ServiceBusMessage("Hello world! " + id);
// send the message
await sender.SendMessageAsync(message);
return "Sent";
Used following code to receive message:
string queueName = "employeequeue";
// since ServiceBusClient implements IAsyncDisposable we create it with "await using"
await using var client = new ServiceBusClient(connectionString);
// create a receiver that we can use to receive and settle the message
ServiceBusReceiver receiver = client.CreateReceiver(queueName);
// the received message is a different type as it contains some service set properties
ServiceBusReceivedMessage receivedMessage = await receiver.ReceiveMessageAsync();
string body = receivedMessage.Body.ToString();
// complete the message, thereby deleting it from the service
await receiver.CompleteMessageAsync(receivedMessage);
More info is available # https://github.com/Azure/azure-sdk-for-net/blob/Azure.Messaging.ServiceBus_7.7.0/sdk/servicebus/Azure.Messaging.ServiceBus/README.md

Microsoft.Azure.ServiceBus.MessagingEntityDisabledException: ReceiveDisabled

I have changed the Azure Service Bus Queue status from Active to ReceiveDisabled. Because I don’t want to process whenever message available in the queue. i.e. reason I have changed status to ReceiveDisabled.
After I changed the queue status to ReceiveDisabled, then I’m not able to post any message to that queue because I’m getting the below error.
Microsoft.Azure.ServiceBus.MessagingEntityDisabledException: Messaging entity 'sb://xxx-xxx-xxx.servicebus.windows.net/test-queue' is currently disabled
.Net Core code to change the queue status:
var serviceBusManagementClient = new ManagementClient(_serviceBusSettings.Connection);
foreach (var queueItem in queueItems)
{
var queueDescription = await serviceBusManagementClient.GetQueueAsync(queueItem.Value);
queueDescription.Status = EntityStatus.ReceiveDisabled;
await serviceBusManagementClient.UpdateQueueAsync(queueDescription);
}
.Net Core code to post messages to queue.
var messageSender = new MessageSender(serviceBusSettings.Connection, serviceBusSettings.MainQueueName);
var message = new Message(Encoding.UTF8.GetBytes(id))
{
//Assign a SessionId for the message
MessageId = id
};
// Send a message corresponding to this sessionId to the queue
messageSender.SendAsync(message);
I’m using the below references.
Microsoft.Azure.ServiceBus
Microsoft.Azure.ServiceBus.Management
Reference documentation https://learn.microsoft.com/en-us/azure/service-bus-messaging/entity-suspend
So, how can I post messages to Queue which is having ReceiveDisabled status?

Error sending request from Azure Function

I have an Azure Function that I have configured that listens for incoming messages to an Azure Service Bus. I can receive the messages without a problem. But when I try to route the request onto another service for processing, I am getting an error stating that the POST data is empty.
public static void Run(BrokeredMessage message, TraceWriter log)
{
log.Info($"C# ServiceBus queue trigger function processed message: {message.MessageId}");
if (message != null)
{
//MessageObjectEntity is a custom object
Common.Entities.MessageObjectEntity messageObject = message?.GetBody<Common.Entities.MessageObjectEntity>();
string msgType = messageObject?.MessageType;
var msgContent = messageObject?.MessageContent; // MessageContent is of type object to allow any object to be sent
using (var client = new HttpClient())
{
string url = $"http://mycompany.azurewebsites.net/api/routingtasks?formname={msgType}";
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(subscriber, token);
HttpContent content = new StringContent((string)msgContent, Encoding.UTF8, "application/json");
var response = client.PostAsync(new Uri(url), content); // at this point content is valid
// I am getting a BadRequest returned here as the target service has not received the POST data
// that was sent in via the content variable
}
log.Info("Completing message.");
}
It appears that the POST data sent in the variable content is not received despite it being sent.
UPDATE
When I inspect the JSON sent to my Azure Function in the logger it looks like this.
{"FormName":"UpdateMileage","FormData":[{"Key":"enteredmileage","Value":100},{"Key":"todaysdate","Value":"01/01/2017"}],"Profile":{"EmailAddress":"unittest#mycompany.co.uk","ID":9999999}}
Which doesn't work.
But if I hard code the following JSON from my Azure Function it works correctly (the double quotes are needed to escape the back-slashes).
"\"{\\\"FormName\\\":\\\"UpdateMileage\\\","\\\"FormData\\\":"[{\\\"Key\\\":\\\"enteredmileage\\\",\\\"Value\\\":100},"{\\\"Key\\\":\\\"todaysdate\\\",\\\"Value\\\":\\\"01/01/2017\\\"}],"\\\"Profile\\\":"{\\\"EmailAddress\\\":\\\"unittest#mycompany.co.uk\\\","\\\"ID\\\":9999999}}\""
The problem therefore appears to be the formatting of the JSON that is being sent from my Azure Function, but I don't how I would convert my JSON into this format.
The problem was caused by the fact that I was sending JSON to my ASP.NET Web API service, but sending it as a string type. This is wrong.
The following article explains the correct approach when sending JSON data as a POST request.

How to be notified of a response message when using RabbitMQ RPC and ServiceStack

Under normal circumstances messages with a response will be published to the response.inq, I understand that and it's a nifty way to notify other parties that "something" has happened. But, when using the RPC pattern, the response goes back to the temp queue and disappears. Is this correct? Is there a convenient way, short of publishing another message, to achieve this behavior, of auto-notification?
The Message Workflow docs in describes the normal message workflow for calling a Service via ServiceStack.RabbitMQ:
Request / Reply
The Request/Reply alters the default message flow by specifying its own ReplyTo address to change the queue where the response gets published, e.g:
const string replyToMq = mqClient.GetTempQueueName();
mqClient.Publish(new Message<Hello>(new Hello { Name = "World" }) {
ReplyTo = replyToMq
});
IMessage<HelloResponse> responseMsg = mqClient.Get<HelloResponse>(replyToMq);
mqClient.Ack(responseMsg);
responseMsg.GetBody().Result //= Hello, World!
When using the Request/Reply pattern no other message is published in any other RabbitMQ topic/queue, to alert other subscribers the client would need to republish the message.
RabbitMqServer callbacks
Another way to find out when a message has been published or received is to use the PublishMessageFilter and GetMessageFilter callbacks on the RabbitMqServer and Client which lets you inspect each message that they sent or received, e.g:
var mqServer = new RabbitMqServer("localhost")
{
PublishMessageFilter = (queueName, properties, msg) => {
//...
},
GetMessageFilter = (queueName, basicMsg) => {
//...
}
};

Servicestack-RabbitMq: Return response type in message headers

Is there any way to add the type of the response dto to the rabbitmq response message's headers collection?
(My consumer is using spring's rabbitmq handler which seems to depend on explicit type information inside the mq header when deserializing.)
Currently servicestack's mq producer already returns serveral headers, such as "content_type='application/json".
I am in need of an additional header, e.g. "typeId"="HelloResponse", so that the consuming web app knows how to deserialize the message, even in RPC cases where the response queue name is some kind of GUID.
Is there some kind of configuration which would enable me to archieve such an behaviour? Or some hook before the message gets published so that I can add the header myself?
I've added support for automatically populating the Message Body Type in RabbitMQ's IBasicProperties.Type as well as adding support for both Publish and GetMessage Filters in this commit.
Here's an example of configuring a RabbitMqServer with custom handlers where you can modify the message and its metadata properties when its published and received:
string receivedMsgApp = null;
string receivedMsgType = null;
var mqServer = new RabbitMqServer("localhost")
{
PublishMessageFilter = (queueName, properties, msg) => {
properties.AppId = "app:{0}".Fmt(queueName);
},
GetMessageFilter = (queueName, basicMsg) => {
var props = basicMsg.BasicProperties;
receivedMsgType = props.Type; //automatically added by RabbitMqProducer
receivedMsgApp = props.AppId;
}
};
mqServer.RegisterHandler<Hello>(m =>
new HelloResponse { Result = "Hello, {0}!".Fmt(m.GetBody().Name) });
mqServer.Start();
Once Configured any message published or received will go through the above handlers, e.g:
using (var mqClient = mqServer.CreateMessageQueueClient())
{
mqClient.Publish(new Hello { Name = "Bugs Bunny" });
}
receivedMsgApp.Print(); // app:mq:Hello.In
receivedMsgType.Print(); // Hello
using (IConnection connection = mqServer.ConnectionFactory.CreateConnection())
using (IModel channel = connection.CreateModel())
{
var queueName = QueueNames<HelloResponse>.In;
channel.RegisterQueue(queueName);
var basicMsg = channel.BasicGet(queueName, noAck: true);
var props = basicMsg.BasicProperties;
props.Type.Print(); // HelloResponse
props.AppId.Print(); // app:mq:HelloResponse.Inq
var msg = basicMsg.ToMessage<HelloResponse>();
msg.GetBody().Result.Print(); // Hello, Bugs Bunny!
}
This change is available from ServiceStack v4.0.33+ that's now available on MyGet.

Resources