Service Fabric cluster. Statefull service. Service Bus queue usage - c#

Full disclosure: I am new to the Service Fabric development. Here is my situation. We have Service Fabric Cluster. We deployed stateful service there. Service has designated Service Bus queue, which it listens to. So all service instances on all nodes in the cluster listening to the same Service Bus queue. Each service instance register OnMessage callback with the Service Bus queue to process message like this:
QueueClient Queue = QueueClient.CreateFromConnectionString(
GetServicebusConnectionString(),
ConfigData.SERVICE_QUEUE_NAME);
if (Queue != null)
{
var options = new OnMessageOptions();
options.AutoComplete = false;
Queue.OnMessage((receivedMessage) =>
ProcessMessage(receivedMessage), options);
}
Now, according to messages in the log it looks like all service instances pick up message, that has been placed in the queue simultaneously. Which does not constitute a good thing.
Question is:
Is it possible to use Service Bus queue in a way when each message from the queue would be picked up only by one service instance?

The default receive mode of queue client is PeekLock, and you set AutoComplete property to false, which will not automatically delete message after the client has received the message. After the lock expires, the message will become available again, and other service instances could receive and process it again.
Is it possible to use Service Bus queue in a way when each message from the queue would be picked up only by one service instance?
You could set AutoComplete property to true, or call Complete method after the client has received and processed the message.
Queue.OnMessage((receivedMessage) =>
{ ProcessMessage(receivedMessage); receivedMessage.Complete(); }, options);

Related

MassTransit RabbitMq Sending Messages

I am not able to figure out on how to specify the Exchange and Queue in my GetSendEndpoint()) task when sending / publishing messages?
As per MassTransit documentation https://masstransit-project.com/usage/producers.html#send you can specify the exchange and queue like
GetSendEndpoint(new Uri("queue:input-queue"))
However, I can only do one or the other?
Is there an alternative way of sending with exchange and queue specified?
I am doing this in Asp.Net Core so here are my configuration:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMassTransit();
services.AddSingleton(p => Bus.Factory.CreateUsingRabbitMq(cfg =>
{
cfg.Host("rabbitmq://localhost", h =>
{
h.Username("admin");
h.Password("admin");
});
}));
services.AddSingleton<IBus>(p => p.GetRequiredService<IBusControl>());
services.AddSingleton<IHostedService, BusService>();
}
And this is how send the message
var endpoint = await _bus.GetSendEndpoint(new Uri(queue:Test.Queue));
await endpoint.Send(new Message()
{
Text = "This is a test message"
});
As you can see I can only specify the queue name.
If you specify an exchange, only the exchange is declared on the broker. The message will be sent directly to the exchange.
"exchange:your-exchange-name"
If you specify a queue, the queue along with an exchange of the same name will be declared and the exchange will be bound to the queue. Messages will be delivered to the exchange of the same name, which will deliver them to the queue.
"queue:your-queue-name"
If you want a different exchange and queue name, you can specify both using:
"exchange:your-exchange-name?bind=true&queue=your-queue-name"
Or you could simplify, but it's a little confusing with two queues:
"queue:your-exchange-name&queue=your-queue-name"
Reading Chris's response in here Mass Transit : No consumer
It seems like Exchanges are created by MassTransit when publishing messages, based on the message types. Publishing does not create any queues. Queues are where messages are stored for delivery to consumers.
and Queues are created when receive endpoints are added to a bus. For the consumers, handlers, and sagas added to a receive endpoint, the exchanges are created and bound so that messages published to the exchanges are received by the receive endpoint (via the queue).
So if my publisher doesn't have a receive endpoint defined then any messages I send will be lost as there will be no queues or binding?
Further reading on here https://groups.google.com/forum/#!topic/masstransit-discuss/oVzZkg1os9o seems to further confirm this.
So based on the above link in order to achieve what I want i.e. to create the exchange and bind it to a queue I will need to specify it in the Uri as such
var sendEndpoint = bus.GetSendEndpoint(new Uri("rabbitmq://localhost/vhost1/exchange1?bind=true&queue=queue1"));
where exchange1 is the Exchange, queue1 is the Queue and bind=true would bind the queue to the exchange.
If sticking to the original MT design a Consumers needs to be running before to setup the exchanges and queues before a Producer can start publishing? This seems to give less flexibility to the Publisher?

So many Service Bus Transient Errors?

We have two windows services that live on a Corporate On-Premise Server and that continually send messages to Azure Service Bus in the cloud. Although the messages do end up on the service bus eventually, there are periods of time where the messages just seem to never make it through for a long stretch of time.
This is causing delay issues for us, as we depend on the message arriving onto the service bus and being processed within a minute. However, as can be seen below, a message can be 'blocked' for stretches of up to 30-40 minutes before making its way through to Azure Service Bus. This happens every day, and almost at some time during every hour.
The errors are mainly one of the following (example logs at end of this post):
A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond 191.239.XX.XXX:443
Error during communication with Service Bus. Check the connection information, then retry.
No such host is known
The request operation did not complete within the allotted timeout of 00:01:10. The time allotted to this operation may have been a portion of a longer timeout. TrackingId:f2db6377-e17d-401a-b339-11fbb51c7bf7, Timestamp:19/05/2017 12:47:36 AM
The way that we send messages to the service bus is as follows, simplified below:
private TopicClient _azureTopic;
...
<Begin Loop>
if (_azureTopic == null)
{
var connectionString = "Endpoint=sb://mynamespace.servicebus.windows.net/;SharedAccessKeyName=managerfiddev;SharedAccessKey=AABBCCDDEEFFGGHHHASDFADFAadfadfdfz=EntityPath=mytopic";
_azureTopic = TopicClient.CreateFromConnectionString(connectionString);
_azureTopic.RetryPolicy = RetryPolicy.NoRetry;
}
var brokeredMessage = new BrokeredMessage(message.Message)
{
MessageId = message.Id.ToString()
};
brokeredMessage.Properties["ReceivedTimestamp"] = DateTime.Now;
_azureTopic.Send(brokeredMessage);
<End Loop>
Note:
There is a deliberate reason why we have a NoRetry policy. Without wanting to add too much noise to the question, the same message that failed will be tried again in the next iteration (it sends the message to subscribers in a round robin fashion).
Example log of errors during a small window of time.
20:31:51 Event.WindowsService Event.WindowsService::PublishAzureServiceBusTopicMessage()
error trying to synchronise message with Azure. Message ID: 1191251
Error during communication with Service Bus. Check the connection
information, then retry.
20:32:00 Event.WindowsService Event.WindowsService::PublishAzureServiceBusTopicMessage()
error trying to synchronise message with Azure. Message ID: 1191251
No such host is known
20:32:00 RFID.WindowsService RFID.WindowsService::PublishAzureServiceBusTopicMessage()
error trying to synchronise message with Azure. Message ID: 1930029
No such host is known
20:32:10 RFID.WindowsService RFID.WindowsService::PublishAzureServiceBusTopicMessage()
error trying to synchronise message with Azure. Message ID: 1930029
No such host is known
20:32:10 Event.WindowsService Event.WindowsService::PublishAzureServiceBusTopicMessage()
error trying to synchronise message with Azure. Message ID: 1191251
No such host is known
20:32:10 RFID.WindowsService RFID.WindowsService::PublishAzureServiceBusTopicMessage()
error trying to synchronise message with Azure. Message ID: 1930029
No such host is known
20:34:00 RFID.WindowsService RFID.WindowsService::PublishAzureServiceBusTopicMessage()
error trying to synchronise message with Azure. Message ID: 1930034
Error during communication with Service Bus. Check the connection
information, then retry.
20:38:34 Event.WindowsService Event.WindowsService::PublishAzureServiceBusTopicMessage()
error trying to synchronise message with Azure. Message ID: 1191269
Error during communication with Service Bus. Check the connection
information, then retry.
20:38:51 RFID.WindowsService RFID.WindowsService::PublishAzureServiceBusTopicMessage()
error trying to synchronise message with Azure. Message ID: 1930043
Error during communication with Service Bus. Check the connection
information, then retry.
Service bus has native retry capabilities on Namespace Manager, Messaging Factory, and Client (see Retry guidance for specific services).
Because it is handling transient exception, you shouldn't have duplicated sent messages.
if you want to retry only once You can configure it like that:
var connectionString = "myconnectionstring";
var client = TopicClient.CreateFromConnectionString(connectionString);
client.RetryPolicy = new RetryExponential(minBackoff: TimeSpan.FromSeconds(2),
maxBackoff: TimeSpan.FromSeconds(2),
maxRetryCount: 1);
This should do the trick.
If you want to ensure deduplication, just google azure servicebus deduplication.

Azure ServiceBus multiple listeners on same machine not working

I am using Azure ServiceBus to process items sent by multiple clients.
I am testing the part which receives these messages and have run 2 listeners side by side. However if I submit 2 items to the queue. Only 1 listener ever works and the second doesn't pick up the other item.
I have however tried running 2 listeners on different machines and they both process the 2 items pushed to the queue.
Is the issue with running multiple listeners on the same machine and if so, what am i doing wrong and how do i fix?
Thanks for your time.
Dan
Is the issue with running multiple listeners on the same machine
I create a console application to receive messages from Service Bus queue, and then I open and run program twice on my machine, both of queue clients/receivers could receive and process messages from same queue.
Call OnMessage method:
var client = QueueClient.CreateFromConnectionString(connectionString, queueName);
var options = new OnMessageOptions();
options.AutoComplete = false;
client.OnMessage(mes =>
{
Console.WriteLine(mes.GetBody<string>());
}, options);
Output:
Call Receive method:
var client = QueueClient.CreateFromConnectionString(connectionString, queueName);
BrokeredMessage mes = client.Receive();
Console.WriteLine(mes.GetBody<string>());
Output:
If possible, please share us your code, and then we will reproduce the issue based on your code.

Azure Service Bus integration with SignalR

This question is not about Scaleout with SignalR and Azure Service Bus. I want to build a Service Bus listener (e.g. OnMessage) into my SignalR web socket app that then distributes the message accordingly to connected users. Messages will be posted from various separately running services into the centralized Service Bus and the UI/browser connected to the web socket servers should receive these.
Option 1: I can add an async Task into a hub method to subscribe to a Service Bus and filter by the connected user. The problem with this is it uses an extra thread from the thread pool and will do this for every socket connection the user has started. Our app can easily start 5-10 or more sockets for every tab open.
Option 2: I can add a single task to the SignalR Startup.Configuration method that then receives all messages and distributes them to the proper connected users. The problem I've encountered here is that I don't have access to the Clients object used for sending to the browser.
I feel like SignalR and Service Bus are a good complement to each other to enable near real-time communications but I can find very little to implement a scenario like this. And I feel like this should be a common enough scenario. Perhaps I'm missing some obvious design patterns that would be a better solution.
I was able to figure this out. In the SignalR Startup.Configuration method I added a method to start the listener and in that method I called GlobalHost.ConnectionManager.GetHubContext. Currently this doesn't send to individual users but I'll add a connection manager of some sort to handle that.
public void startServiceBusListener()
{
// setup subcsription
var namespaceManager = NamespaceManager.CreateFromConnectionString(connectionString);
if (!namespaceManager.SubscriptionExists("myTopic", Environment.MachineName))
namespaceManager.CreateSubscription("myTopic", Environment.MachineName);
SubscriptionClient busClient = SubscriptionClient.CreateFromConnectionString(connectionString, "myTopic", Environment.MachineName);
// Configure the callback options.
OnMessageOptions options = new OnMessageOptions();
options.AutoComplete = false;
options.AutoRenewTimeout = TimeSpan.FromMinutes(1);
receiveTask = Task.Run(() =>
{
// handle new messages
busClient.OnMessage((message) =>
{
try
{
Notification note = message.GetBody<Notification>();
string notification = JsonConvert.SerializeObject(note);
GlobalHost.ConnectionManager.GetHubContext<DispatchHub>().Clients.All.notify(notification);
// Remove message from subscription.
message.Complete();
}
catch (Exception)
{
// Indicates a problem, unlock message in subscription.
message.Abandon();
}
}, options);
}, cts.Token);
}

Getting data from 50 service bus queue for a real time dashboard in a azure web app

Using the code as shown here.. I was able to create a web app that every 30 seconds sent data to client using System.Threading.Timer.
I was able to add some code which received data from a service bus queue using Messaging factory and Messaging receiver and based on that sent data to signalR client instead of hard-coding as in the mentioned example.
Now my real application gets data from 50 such queue..
Theoretically, I could create 50 timer objects which would call 50 different methods which in turn would call service bus queue.
I would sincerely appreciate if someone could suggest the right way to achieve my goal..
Thanks
The message pump pattern seems like it would be a good fit for this application. You create a separate client for each queue and configure each one to automatically listen for messages in its queue and process them as they come in.
foreach (var queueName in queueNames){
var queueClient = QueueClient.CreateFromConnectionString(connectionString, queueName);
queueClient.OnMessage(message =>
{
// Do work here
Console.Out.WriteLine(string.Format("Recieved message {0} on queue {1}", message.MessageId, queueName));
});
}

Categories

Resources