I am trying out Azure Service Bus queue. I have the below code:
Queue send:
string strConnectionString = ConfigurationManager.AppSettings["Microsoft.ServiceBus.ConnectionString"];
var namespaceManager = NamespaceManager.CreateFromConnectionString(strConnectionString);
if (!namespaceManager.QueueExists("Test"))
{
QueueDescription qD = new QueueDescription("Test");
qD.DefaultMessageTimeToLive = new TimeSpan(05, 00, 00);
qD.LockDuration = new TimeSpan(00, 02, 30);
qD.MaxSizeInMegabytes = 5120;
namespaceManager.CreateQueue(qD);
}
if (namespaceManager.QueueExists("Test"))
{
QueueClient client = QueueClient.CreateFromConnectionString(strConnectionString, "Test", ReceiveMode.PeekLock);
var qMessage = Console.ReadLine();
using (MemoryStream strm = new MemoryStream(Encoding.UTF8.GetBytes(qMessage)))
{
BrokeredMessage bMsg = new BrokeredMessage(strm);
bMsg.MessageId = Guid.NewGuid().ToString();
bMsg.TimeToLive = new TimeSpan(05, 00, 00);
client.Send(bMsg);
Console.WriteLine("Message sent");
}
}
Console.ReadLine();
The receive code:
string strConnectionString = ConfigurationManager.AppSettings["Microsoft.ServiceBus.ConnectionString"];
var namespaceManager = NamespaceManager.CreateFromConnectionString(strConnectionString);
if (namespaceManager.QueueExists("Test"))
{
QueueClient client = QueueClient.CreateFromConnectionString(strConnectionString, "Test",ReceiveMode.PeekLock);
if (client != null)
{
OnMessageOptions options = new OnMessageOptions();
options.AutoComplete = false;
options.AutoRenewTimeout = TimeSpan.FromSeconds(31);
client.OnMessage((message) =>
{
Console.WriteLine(message.State.ToString());
Console.WriteLine("Message Id: " + message.MessageId);
Stream stream = message.GetBody<Stream>();
StreamReader reader = new StreamReader(stream);
Console.WriteLine("Message: " + reader.ReadToEnd());
Console.WriteLine("***************");
message.Abandon();
});
Console.ReadLine();
}
}
I see that whenever I call Abandon, the message is getting DeadLettered. My assumption was that it should get Active and can be picked up by another client.
Your understanding of BrokeredMessage.Abandon Api is correct. It is intended to abandon the peek-lock acquired on the message (but NOT abandon the message itself) and hence, makes it available for other receivers to pick the Message up.
Here's how we envisioned different states of a peek-lock'ed message:
Basics first
The 'Why': If Customers need Competing-Consumer (Job-Queue) semantics - where they need multiple workers to simultaneously process different messages from a Queue with Exactly-Once guarantee - then they use the ReceiveMode.PeekLock. In this model, each worker (the queue receiver) needs a way to communicate the Progress of its Current message (Job) to other workers. Hence, brokeredMessage provides 4 functions to express the states.
The 'What':
if a message is successfully processed by the current Worker - call BrokeredMessage.Complete()
if the BrokeredMessage cannot be processed by the current worker, and want the processing to be retried on another Worker - then, Abandon the message. But, the catch here is: lets say, there are 2 workers and each of them thinks that the other one can process this message and calls Abandon - soon they will end up in an Infinite loop of retry'ing to process just that message! So, to avoid this situation, we provided a Configuration called MaxDeliveryCount on QueueDescription. This setting guards the limit on the number of times the message is delivered from the Queue to receiver. In the above example, Each time you received (and abandoned) the message, the 'deliveryCount' on the ServiceBus service is incremented. When it reaches 10 - the message has hit max no. of deliveries and hence, will be deadlettered.
if the current receiver (worker) knows for sure, that, this message cannot be processed, BrokeredMessage.DeadLetter(). The goal here is to let the consuming application Audit the dead-lettered messages regularly.
if the current receiver (worker) cannot process this message, but, knows that this message can be processed at a later point of time BrokeredMessage.Defer().
HTH!
Sree
Related
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;
}
Question
How do i get the dead letter queue length without receiving each message and counting how many message I received?
My Current Implementation
public int GetDeadLetterQueueCount()
{
//Ref:http://stackoverflow.com/questions/22681954/how-do-you-access-the-dead-letter-sub-queue-on-an-azure-subscription
MessagingFactory factory = MessagingFactory.CreateFromConnectionString(CloudConnectionString);
QueueClient deadLetterClient = factory.CreateQueueClient(QueueClient.FormatDeadLetterPath(_QueueClient.Path), ReceiveMode.PeekLock);
BrokeredMessage receivedDeadLetterMessage;
List<string> lstDeadLetterQueue = new List<string>();
// Ref: https://code.msdn.microsoft.com/Brokered-Messaging-Dead-22536dd8/sourcecode?fileId=123792&pathId=497121593
// Log the dead-lettered messages that could not be processed:
while ((receivedDeadLetterMessage = deadLetterClient.Receive(TimeSpan.FromSeconds(10))) != null)
{
lstDeadLetterQueue.Add(String.Format("DeadLettering Reason is \"{0}\" and Deadlettering error description is \"{1}\"",
receivedDeadLetterMessage.Properties["DeadLetterReason"],
receivedDeadLetterMessage.Properties["DeadLetterErrorDescription"]));
var locktime = receivedDeadLetterMessage.LockedUntilUtc;
}
return lstDeadLetterQueue.Count;
}
Problem with implementation
Because I am receiving each message in peek and block mode, the messages have a lock duration set. During this time i cannot receive or even see the messages again until this time period has timed out.
There must be an easier way of just getting the count without having to poll the queue?
I do not want to consume the messages either, i would just like the count of the total amount.
You can use the NamespaceManager's GetQueue() method which has a MessageCountDetails property, which in turn has a DeadLetterMessageCount property. Something like:
var namespaceManager = Microsoft.ServiceBus.NamespaceManager.CreateFromConnectionString("<CONN_STRING>");
var messageDetails = namespaceManager.GetQueue("<QUEUE_NAME>").MessageCountDetails;
var deadLetterCount = messageDetails.DeadLetterMessageCount;
MessageType: "PublishX"
Consumers:
Type1ConsumerX
Type2ConsumerX
Type3ConsumerX
All of the consumers must catch messages immediately, but consume synchronously inside themselves..
For example there are 100 "PublishX" messages in the queue. Type1ConsumerX consumed 30 messages (synchronously), Type2ConsumerX consumed 50 messages(synchronously) , Type3ConsumerX consumed 100 messages(synchronously).
How can I know the message is consumed by "all type of consumers" ?
Could RabbitMQ/MassTransit PUSH messages to consumers?
Could RabbitMQ/MassTransit push messages (merging them) with intervals (1s) for decrease network traffic?
Could RabbitMQ/MassTransit push same messages to the different type of Consumers?
If I've understood the question correctly you just need to set up a basic pub/sub pattern. This will allow you to deliver the same message to multiple consumers.
Example publisher:
public static void PublishMessageToFanout()
{
var factory = new ConnectionFactory { HostName = "localhost" };
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.ExchangeDeclare("messages", "fanout");
var message = new Message { Text = "This is a message to send" };
var json = JsonConvert.SerializeObject(message);
var body = Encoding.UTF8.GetBytes(json);
channel.BasicPublish("messages", string.Empty, null, body);
}
}
Example consumers:
SubscribeToMessages("sms-messages", (s) => Console.WriteLine("SMS Message: {0}", s));
SubscribeToMessages("email-messages", (s) => Console.WriteLine("Email Message: {0}", s));
public static void SubscribeToMessages(string queueName, Action<string> messageAction)
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.ExchangeDeclare("messages", "fanout");
channel.QueueDeclare(queueName, true, false, false, null);
channel.QueueBind(queueName, "messages", string.Empty);
var consumer = new QueueingBasicConsumer(channel);
channel.BasicConsume(queueName, true, consumer);
while (true)
{
var ea = consumer.Queue.Dequeue();
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
messageAction(message);
}
}
}
If you run the SubscribeToMessages loops in separate processes or console apps you'll see that they both print out the message whenever you call the PublishMessageToFanout. You'll also see that both queues exist in RabbitMQ Management under Queues.
Regarding the MassTransit part of your question
RabbitMQ/MassTransit PUSH messages to consumers?
Yes, MassTransit publishes messages to the bus, and then a consumer processes them
Could RabbitMQ/MassTransit push messages (merging them) with intervals (1s) for decrease network traffic?
Don't know if there is a feature for this, you could write your own but you would have to be very careful about losing the messages.
Could RabbitMQ/MassTransit push same messages to the different type of Consumers?
Yes, multiple consumers can consume the same type of message.
I've written a simple hello world app that shows the basics - http://nodogmablog.bryanhogan.net/2015/04/mass-transit-with-rabbitmq-hello-world/
I have a legacy Windows service running on Server 2008 that reads messages from a Transactional MSMQ Queue. This is not configured as a WCF service.
We are wanting to improve the handling of failed and poison messages in code (C# 4.0) by catching custom exceptions and sending the related message to a separate 'failed' or 'poison' queue depending upon the type of exception thrown.
I can't get the Catch code to send the message to the separate queue - it disappears from the original queue (as desired!) but doesn't show up in the 'failed' queue.
For testing all of the queues have no Authentication required and permissions are set to allow Everyone to do everything.
Clearly something is missing or wrong and I suspect it is transaction related, but I can't see it. Or perhaps this is not possible the way I am trying to do it ?
Any guidance / suggestions appreciated!
Our simplified PeekCompleted Event code:
private void MessageReceived(object sender, PeekCompletedEventArgs e)
{
using (TransactionScope txnScope = new TransactionScope())
{
MyMessageType currentMessage = null;
MessageQueue q = ((MessageQueue)sender);
try
{
Message queueMessage = q.EndPeek(e.AsyncResult);
currentMessage = (FormMessage)queueMessage.Body;
Processor distributor = new Processor();
Processor.Process(currentMessage); // this will throw if need be
q.ReceiveById(e.Message.Id);
txnScope.Complete();
q.BeginPeek();
}
catch (MyCustomException ex)
{
string qname = ".\private$\failed";
if (!MessageQueue.Exists(qname)){
MessageQueue.Create(qname , true);
}
MessageQueue fq = new MessageQueue(qname){
Formatter = new BinaryMessageFormatter()
};
System.Messaging.Message message2 = new System.Messaging.Message{
Formatter = new BinaryMessageFormatter(),
Body = currentMessage,
Label = "My Failed Message";
};
fq.Send(message2); //send to failed queue
q.ReceiveById(e.Message.Id); //off of original queue
txnScope.Complete(); // complete the trx
_queue.BeginPeek(); // next or wait
}
//other catches handle any cases where we want to tnxScope.Dispose()
EDIT : October 8, 2013
Hugh's answer below got us on the right track. Inside the Catch block the Failed Queue was already created as transactional
MessageQueue.Create(qname , true);
but the Send needed a TransactionType parameter
fq.Send(message2,MessageQueueTransactionType.Single);
That did the trick. Thanks Hugh!
If the message is disappearing from your original queue then that means your code is reaching the second scope.Complete() (in your catch block).
This means the problem has to do with your send to the error queue.
I would suggest that you need to create the queue as transactional because you are sending from within a scope.
MessageQueue fq = new MessageQueue(qname, true){
Formatter = new BinaryMessageFormatter()
};
Then you need to do a transactional send:
fq.Send(message2, Transaction.Current);
See if this works.
I am using Rabbit MQ in C#. This is my scenario
A separate process publishes messages to the queue
Client has to read set of N messages from queue
Process the N messages
Acknowledge the N messages
Under the same channel, I receive the messages and then process them and then acknowledge them. The server process keeps publishing messages. The problem I am facing is, when I try to get next set of messages, they do not come in the same order as it was published by the publishing process. The messages come in a random order. Only the first set of messages come in the correct order.
Does any one what is going wrong here? Is creating a new channel to access the next set of messages not right? Below is the sample code:
while (true)
{
using (IModel getChannel = MQConnection.CreateModel())
{
// Create a consumer
QueueingBasicConsumer consumer = CreateQueueConsumer(getChannel, exchangeName, queueName);
int numberOfMessages = 100;
// Next Recieve
List<object> msgSet = GetNextSetOfMessages(consumer, getChannel, exchangeName, queueName, numberOfMessages, out finalDeliverytag);
// Do some processing
if (finalDeliverytag > 0)
AckFinishedMessages(exchangeName, queueName, finalDeliverytag, getChannel);
if (finalDeliverytag == 0)
break;
}
}
Kindly help. Thanks!