Get queue name from inside QueueClient.OnMessage callback - c#

QueueClient.OnMessage takes a callback, Action<BrokeredMessaged>, as an argument that will be executed by an internal message pump that's constantly polling a queue (or subscription) when a message is available.
I've been looking at the BrokeredMessage type in Reflector but can't find a way to get the queue name that the message came from the BrokeredMessage object (that last part is key). If this is possible, how can it be pulled out?

Finally figured out a solution using reflection:
public void OnMessageCallback(BrokeredMessage message) {
var context = message.GetType().GetProperty("ReceiveContext", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(message);
var receiver = (MessageReceiver)context.GetType().GetProperty("MessageReceiver", BindingFlags.Public | BindingFlags.Instance).GetValue(context);
var queueName = receiver.Path;
}

If you are using the QueueClient.OnMessage, you can do something like that:
var client = QueueClient.CreateFromConnectionString("MyConnectionString");
client.OnMessage(message =>
{
// You always have access to the queue path
var queueName = client.Path;
});
If you don't want to use anonymous function you can pass the queueName to the function that is going to process you message:
public void ProcessMessage(BrokeredMessage message, string queueName)
{
}
And call you function like that:
var client = QueueClient.CreateFromConnectionString("MyConnectionString");
client.OnMessage(message =>
{
ProcessMessage(message , client.Path);
});
EDIT : Using a MessageReceiver
Azure ServiceBus SDK provides an abstraction to receive messages from queues or subscriptions:
var messagingFactory = MessagingFactory.CreateFromConnectionString("MyConnectionString");
var messageReceiver = messagingFactory.CreateMessageReceiver("MyQueueName");
messageReceiver.OnMessage(message =>
{
// You always have access to the queue path
var queueName = messageReceiver.Path;
}, new OnMessageOptions());

Related

Azure Service Bus Receive Messages continuously when ever new message placed in web application [duplicate]

I am using Azure.Messaging.ServiceBus nuget package to work with Azure service bus. We have created a topic and a subscription. The subscription has 100+ messages. We want to read all the message and continue to read message as they arrive.
Microsoft.Azure.ServiceBus package (deprecated now) provided RegisterMessageHandler which use to process every incoming message. I am not able to find similar option under Azure.Messaging.ServiceBus nuget package.
I am able to read one message at a time but I have to call await receiver.ReceiveMessageAsync(); every time manually.
To receive multiple messages (a batch), you should use ServiceBusReceiver.ReceiveMessagesAsync() (not plural, not singular 'message'). This method will return whatever number of messages it can send back. To ensure you retrieve all 100+ messages, you'll need to loop until no messages are available.
If you'd like to use a processor, that's also available in the new SDK. See my answer to a similar question here.
As suggested by #gaurav Mantri, I used ServiceBusProcessor class to implement event based model for processing messages
public async Task ReceiveAll()
{
string connectionString = "Endpoint=sb://sb-test-today.servicebus.windows.net/;SharedAccessKeyName=manage;SharedAccessKey=8e+6SWp3skB3Aedsadsadasdwz5DU=;";
string topicName = "topicone";
string subscriptionName = "subone";
await using var client = new ServiceBusClient(connectionString, new ServiceBusClientOptions
{
TransportType = ServiceBusTransportType.AmqpWebSockets
});
var options = new ServiceBusProcessorOptions
{
// By default or when AutoCompleteMessages is set to true, the processor will complete the message after executing the message handler
// Set AutoCompleteMessages to false to [settle messages](https://learn.microsoft.com/en-us/azure/service-bus-messaging/message-transfers-locks-settlement#peeklock) on your own.
// In both cases, if the message handler throws an exception without settling the message, the processor will abandon the message.
AutoCompleteMessages = false,
// I can also allow for multi-threading
MaxConcurrentCalls = 1
};
await using ServiceBusProcessor processor = client.CreateProcessor(topicName, subscriptionName, options);
processor.ProcessMessageAsync += MessageHandler;
processor.ProcessErrorAsync += ErrorHandler;
await processor.StartProcessingAsync();
Console.ReadKey();
}
public async Task MessageHandler(ProcessMessageEventArgs args)
{
string body = args.Message.Body.ToString();
Console.WriteLine(body);
// we can evaluate application logic and use that to determine how to settle the message.
await args.CompleteMessageAsync(args.Message);
}
public Task ErrorHandler(ProcessErrorEventArgs args)
{
// the error source tells me at what point in the processing an error occurred
Console.WriteLine(args.ErrorSource);
// the fully qualified namespace is available
Console.WriteLine(args.FullyQualifiedNamespace);
// as well as the entity path
Console.WriteLine(args.EntityPath);
Console.WriteLine(args.Exception.ToString());
return Task.CompletedTask;
}

How to programmatically resend messages that have faulted with EasyNetQ?

I am wondering how to resend messages that have faulted with EasyNetQ, programmatically, without using HosePipe, that is to resend to their original target queue using their original sending exchange.
Is it possible, how?
The solution I came up with is:
public static class AdvancedBusExtensions
{
public static async Task ResendErrorsAsync(this IAdvancedBus source, string errorQueueName)
{
var errorQueue = await source.QueueDeclareAsync(errorQueueName);
var message = await source.GetMessageAsync(errorQueue);
while (message != null)
{
var utf8Body = Encoding.UTF8.GetString(message.Body);
var error = JsonConvert.DeserializeObject<Error>(utf8Body);
var errorBodyBytes = Encoding.UTF8.GetBytes(error.Message);
var exchange = await source.ExchangeDeclareAsync(error.Exchange, x =>
{
// This can be adjusted to fit the exchange actual configuration
x.AsDurable(true);
x.AsAutoDelete(false);
x.WithType("topic");
});
await source.PublishAsync(exchange, error.RoutingKey, true, error.BasicProperties, errorBodyBytes);
message = await source.GetMessageAsync(errorQueue);
}
}
}

MassTransit: Initialize consumer constructor with IRequestClient

1) Hi. I'm learning MassTransit with RabbitMQ, but stuck with using Request/Respond. I read a lot of articles and try to write console app using MassTransit documentation. But still can't find any information about initializing consumer with IRequestClient interface. Here is My code:
static void Main(string[] args){
var serviceAddress = new Uri("loopback://localhost/notification.service");
var requestTimeout = TimeSpan.FromSeconds(120);
var bus = BusConfigurator.ConfigureBus((cfg, host) =>
{
cfg.ReceiveEndpoint(host, RabbitMqConstants.NotificationServiceQueue, e =>
{
e.Consumer(() => new OrderRegisteredConsumer(???));
});
});
IRequestClient<ISimpleRequest, ISimpleResponse> client = new MessageRequestClient<ISimpleRequest, ISimpleResponse>(bus, serviceAddress, requestTimeout);
bus.Start();
Console.WriteLine("Listening for Order registered events.. Press enter to exit");
Console.ReadLine();
bus.Stop();
}
And my consumer
public class OrderRegisteredConsumer: IConsumer<IOrderRegisteredEvent>
{
private static IBusControl _bus;
IRequestClient<ISimpleRequest, ISimpleResponse> _client;
public OrderRegisteredConsumer(IRequestClient<ISimpleRequest, ISimpleResponse> client)
{
_client = client;
}
public async Task Consume(ConsumeContext<IOrderRegisteredEvent> context)
{
await Console.Out.WriteLineAsync($"Customer notification sent: Order id {context.Message.OrderId}");
ISimpleResponse response = await _client.Request(new SimpleRequest(context.Message.OrderId.ToString()));
Console.WriteLine("Customer Name: {0}", response.CustomerName);
}
}
How can I put my client inside
e.Consumer(() => new OrderRegisteredConsumer(???));
2) I Also try to find some information about Request/Respond in sagas, but, unfortunately, all I find is https://github.com/MassTransit/MassTransit/issues/664
I will appreciate If someone have an example of using this in sagas, or if someone could provide some links, where I can read about this more.
You need the client variable to be available, but the client doesn't need to be ready at the moment you configure the endpoint. endpoint.Consumer does not instantiate the consumer straight away, it just needs a factory delegate, which will instantiate the consumer when a message comes for this consumer.
Since the delegate is a reference type, you can assign it later in your code.
So this would work:
IRequestClient<ISimpleRequest, ISimpleResponse> client;
var bus = BusConfigurator.ConfigureBus((cfg, host) =>
{
cfg.ReceiveEndpoint(host, RabbitMqConstants.NotificationServiceQueue, e =>
{
e.Consumer(() => new OrderRegisteredConsumer(client));
});
});
client = new MessageRequestClient<ISimpleRequest, ISimpleResponse>(
bus, serviceAddress, requestTimeout);

async/await events

i have following scenario. I am running a tcp client where i get push notifications like heartbeats and other objects. I can also run commands against the server like GetWeather infos or something like that. Everytime i receive an
object i raise an event which works pretty fine.
But now i want to be able to request some data on the server and wait for it until the server responses the right object. During the request of an object other objects can also be send to me.
Here is some pseudocode:
Instead of:
TcpServer.ObjectReceived += ObjectReceivedMethod;
TcpServer.GetWeather();
public void ObjectReceived(object data)
{
}
I want:
var result = await TcpServer.GetWeather();
How can i transfer the Weather Info from ObjectReceived to the awaiting method?
KR Manuel
You want to use a TaskCompletionSource<T>, something like this:
private Dictionary<Guid, TaskCompletionSource<WeatherResponse>> _weatherRequests;
public Task<WeatherResponse> GetWeatherAsync()
{
var messageId = Guid.NewGuid();
var tcs = new TaskCompletionSource<WeatherResponse>();
_weatherRequests.Add(messageId, tcs);
_server.SendWeatherRequest(messageId);
return tcs.Task;
}
public void ObjectReceived(object data)
{
...
if (data is ServerWeatherResponse)
{
var tcs = _weatherRequests[data.requestId];
_weatherRequests.Remove(data.requestId);
tcs.SetResult(new WeatherResponse(data));
}
}
This assumes that your server will associate requests with responses using a GUID id.

.NET Client - Waiting for an MQTT response before proceeding to the next request

I have a MQTT calls inside a loop and in each iteration, it should return a response from the subscriber so that I could use the value being forwarded after I published. But the problem is I don't know how would I do it.
I hope you have an idea there or maybe if I'm just not implementing it right, may you guide me through this. Thanks.
Here's my code:
// MyClientMgr
class MyClientMgr{
public long CurrentOutput { get; set; }
public void GetCurrentOutput(MyObjectParameters parameters, MqttClient client)
{
MyMessageObject msg = new MyMessageObject
{
Action = MyEnum.GetOutput,
Data = JsonConvert.SerializeObject(parameters)
}
mq_GetCurrentOutput(msg, client);
}
private void mq_GetCurrentOutput(MyMessageObject msg, MqttClient client)
{
string msgStr = JsonConvert.SerializeObject(msg);
client.Publish("getOutput", Encoding.UTF8.GetBytes(msgStr),
MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE, false);
client.MqttMsgPublishReceived += (sender, e) =>{
MyObjectOutput output = JsonConvert.DeserializeObject<MyObjectOutput>(Encoding.UTF8.GetString(e.Message));
CurrentOutput = output;
};
}
}
// MyServerMgr
class MyServerMgr
{
public void InitSubscriptions()
{
mq_GetOutput();
}
private void mq_GetOutput()
{
MqttClient clientSubscribe = new MqttClient(host);
string clientId = Guid.NewGuid().ToString();
clientSubscribe.Connect(clientId);
clientSubscribe.Subscribe(new string[] { "getOutput" }, new byte[] { MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE });
MqttClient clientPublish = new MqttClient(host);
string clientIdPub = Guid.NewGuid().ToString();
clientPublish.Connect(clientIdPub);
clientSubscribe.MqttMsgPublishReceived += (sender, e) => {
MyMessageObj msg = JsonConvert.DeserializeObject<MyMessageObj>(Encoding.UTF8.GetString(e.Message));
var output = msg.Output;
clientPublish.Publish("getOutput", Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(output)), MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE, false);
}
}
}
// MyCallerClass
class MyCallerClass
{
var host = "test.mqtt.org";
var myServer = new MyServerMgr(host);
var myClient = new MyClientMgr();
myServer.InitSubscriptions();
MqttClient client = new MqttClient(host);
for(int i = 0; i < 10; i++)
{
long output = 0;
MyObjectParameters parameters = {};
myClient.GetCurrentOutput(parameters, client) // here I call the method from my client manager
// to publish the getting of the output and assigned
// below for use, but the problem is the value doesn't
// being passed to the output variable because it is not
// yet returned by the server.
// Is there a way I could wait the process to
// get the response before assigning the output?
output = myClient.CurrentOutput; // output here will always be null
// because the response is not yet forwarded by the server
}
}
I have a loop in my caller class to call the mqtt publish for getting the output, but I have no idea how to get the output before it was assigned, I want to wait for the response first before going to the next.
I've already tried doing a while loop inside like this:
while(output == 0)
{
output = myClient.CurrentOutput;
}
Yes, I can get the output here, but it will slow down the process that much. And sometimes it will fail.
Please help me. Thanks.
It looks like you are trying to do synchronous communication over an asynchronous protocol (MQTT).
By this I mean you want to send a message and then wait for a response, this is not how MQTT works as there is no concept of a reply to a message at the protocol level.
I'm not that familiar with C# so I'll just give an abstract description of possible solution.
My suggestion would be to use a publishing thread, wait/pulse (Look at the Monitor class) to have this block after each publish and have the message handler call pulse when it has received the response.
If the response doesn't contain a wait to identify the original request you will also need a state machine variable to record which request is in progress.
You may want to look at putting a time out on the wait in case the other end does not respond for some reasons.
You can use AutoResetEvent class that has WaitOne() and Set() methods. Using WaitOne() after publish will wait until the message is published and using Set() under client_MqttMsgPublishReceived event will release the wait when the subscriber received the message he subscribed for.

Categories

Resources