This is my publisher. There are two consumers. MailConsumer and SmsConsumer.
using(var bus = RabbitHutch.CreateBus("host=localhost").Advanced) {
var queueName = "my.queue";
var queueName2 = "my.queue2";
var queue = bus.QueueDeclare(queueName);
var queue2 = bus.QueueDeclare(queueName2);
var channel = bus.ExchangeDeclare("MyFanout", ExchangeType.Fanout);
bus.Bind(channel, queue, "sms");
bus.Bind(channel, queue2, "mail");
var input = "";
Console.WriteLine("Enter a message. 'q' to quit.");
while((input = Console.ReadLine()) != "q") {
for(int i = 1; i < 2; i++) {
var message = new Message<TextMessage>(new TextMessage {
Text = input + i
});
bus.Publish(channel, "", false, false, message);
}
}
}
I can subscribe with this code:
using(var bus = RabbitHutch.CreateBus("host=localhost").Advanced) {
var queueName = "my.queue2";
var queue = bus.QueueDeclare(queueName);
bus.Consume(queue, x => x.Add<TextMessage>((message, info) => {
Thread.Sleep(1000);
Console.WriteLine("SMS: {0}", message.Body.Text);
}));
Console.WriteLine("Listening for messages. Hit <return> to quit.");
Console.ReadLine();
}
How can I achieve it via AutoSubscriber? There is no option for Queue Name in AutoSubscriber, there is "Subscription Id"
There's a property on the AutoSubscriber called 'GenerateSubscriptionId', which you can set to generate the subscription id for a consumer:
subscriber.GenerateSubscriptionId = subscriptionInfo =>
{
return "MyApplication:" + subscriptionInfo.ConcreteType.Name);
};
Then the subscription id will be used by the default conventions to generate a queue name.
Update:
I think you can achieve what you want just with normal pub-sub without declaring any queue.
In your publisher you can do this:
var bus = RabbitHutch.CreateBus("host=localhost")
while((input = Console.ReadLine()) != "q") {
for(int i = 1; i < 2; i++) {
var message = new Message<TextMessage>(new TextMessage {
Text = input + i
});
bus.Publish(message);
}
}
Then just create 2 consumers to subscribe to your message, and your message will be handled by both:
class SmsConsumer : IConsume<TextMessage>{
...
}
class LogConsumer : IConsume<TextMessage>{
...
}
and in your startup:
var bus = RabbitHutch.CreateBus("host=localhost")
var subscriber = new AutoSubscriber(bus,"my_applications_subscriptionId_prefix");
subscriber.Subscribe(Assembly.GetExecutingAssembly());
Console.ReadLine()
EasyNetQ will declare the exchange, queues and bindings for you. Just make sure your TextMessage class is in an assembly shared by both projects.
Related
internal class Program2
{
public static async Task<int> Main(string[] args)
{
Console.WriteLine("String console app");
var senderObject = MessageActivator.GetSenderObject();
MessageOptions opts = null;
Parser.Default.ParseArguments<MessageOptions>(args).WithParsed(opts1 =>
{
opts = (MessageOptions)opts1;
}).WithNotParsed(optsx =>
{
Environment.Exit(0);
});
await Task.Delay(100);
senderObject.SendMessage(new SyslogMessageOptions()
{
SyslogServerHost = opts.SyslogServerHost,
SyslogServerPort = opts.SyslogServerPort,
ApplicationName = opts.ApplicationName,
ProcessorId = opts.ProcessorId,
MessageId = opts.MessageId,
Facility = opts.Facility,
Severity = opts.Severity,
NetworkProtocols = (SyslogTesterLib.NetworkProtocol)opts.NetworkProtocols,
MessageFormat = (SyslogTesterLib.SyslogMessageFormat)opts.MessageFormat,
Message = opts.Message,
ShouldPrintMessage = opts.ShouldPrintMessage,
LocalHostName = opts.LocalHostName,
StructuredData = opts.StructuredData,
Date = opts.Date,
}, new BulkMessageOptions()
{
TotalNMessageOptions = new TotalNMessageOptions() { TotalMessages=opts.TotalNMessageCount},
BulkMessageSendType = BulkMessageSendType.OncePerSecond,
});
Console.WriteLine("Press any key to close.");
Console.ReadKey();
return 0;
}
}
Here I am sending a syslog bulk message with various options.
What I wanted to know is, is it possible for it to automatically send every 1 second. If yes, how can I possibly do that?
There's the codes, in case if it brings of any help.
See the Interval property of Timer object.
https://learn.microsoft.com/en-us/dotnet/api/system.timers.timer.interval?view=net-6.0
Just set your interval and assign your callback to the elapsed event
//send a message every 1 second
var timer = new Timer(1000);
var senderObject = MessageActivator.GetSenderObject();
MessageOptions opts = null;
Parser.Default.ParseArguments<MessageOptions>(args).WithParsed(opts1 =>
{
opts = (MessageOptions)opts1;
});
timer.Elapsed += (sender, e) =>
{
senderObject.SendMessage(new SyslogMessageOptions()
{
SyslogServerHost = opts.SyslogServerHost,
SyslogServerPort = opts.SyslogServerPort,
ApplicationName = opts.ApplicationName,
ProcessorId = opts.ProcessorId,
MessageId = opts.MessageId,
Facility = opts.Facility,
Severity = opts.Severity,
NetworkProtocols = (SyslogTesterLib.NetworkProtocol)opts.NetworkProtocols,
MessageFormat = (SyslogTesterLib.SyslogMessageFormat)opts.MessageFormat,
Message = opts.Message,
ShouldPrintMessage = opts.ShouldPrintMessage,
LocalHostName = opts.LocalHostName,
StructuredData = opts.StructuredData,
Date = opts.Date,
});
};
timer.Start();
Console.ReadLine();
I'd like to peek all messages from several Azure Service Bus queues. After that I want to filter them after queueName, insertDate and give the opportunity to make a full text search on the body.
Currently, I'm using the Microsoft.Azure.ServiceBus package to create a ManagementClient for gathering queue information and then use a MessageReceiver to peek the messages.
var managementClient = new ManagementClient(connectionString);
var queue = await managementClient.GetQueueRuntimeInfoAsync(queueName);
var count = queue.MessageCount;
var receiver = new MessageReceiver(connectionString, queueName);
var messagesOfQueue = new List<Message>();
for (var i = 1; i <= count; i++)
{
messagesOfQueue.Add(await receiver.PeekAsync());
}
Is there a better way to get all messages? Or is there even a way to only peek messages that apply to a filter?
I've also tried to use the QueueClient.PeekBatch Method from the WindowsAzure.ServiceBus package. But that method didn't return all messages although I've set the correct messageCount parameter.
And then there is also the package Azure.Messaging.ServiceBus... What's up with all these packages?
So which of the packages should I use and what is the best way for peeking messages of queues based on some filters?
The solution I'm currently using and which works as expected looks like this:
var receiver = serviceBusClient.CreateReceiver(queueName);
var messagesOfQueue = new List<ServiceBusReceivedMessage>();
var previousSequenceNumber = -1L;
var sequenceNumber = 0L;
do
{
var messageBatch = await receiver.PeekMessagesAsync(int.MaxValue, sequenceNumber);
if (messageBatch.Count > 0)
{
sequenceNumber = messageBatch[^1].SequenceNumber;
if (sequenceNumber == previousSequenceNumber)
break;
messagesOfQueue.AddRange(messageBatch);
previousSequenceNumber = sequenceNumber;
}
else
{
break;
}
} while (true);
It uses the nuget package Azure.Messaging.ServiceBus.
Currently you're receiving a single message from the receiver. A better option would be to receive messages in batch using PeekBatchAsync(Int64, Int32) method of MessageReceiver.
Here's the sample code to do so (untested though):
var messagesOfQueue = new List<Message>();
var sequenceNumber = 0;
var batchSize = 100;//number of messages to receive in a single call
do
{
var messages = await receiver.PeekBatchAsync(sequenceNumber, batchSize);
messagesOfQueue.AddRange(messages);
if (messages.Count > 0)
{
sequenceNumber = messages[messages.Count-1].SequenceNumber;
}
else
{
break;
}
} while (true);
The solution avoids getting the message with the same SequenceNumber twice.
Sequence numbers monotonically increase. And I've tested most cases except rolling over sequenceNumber to 0 when it reaches the maximum value (Long.MaxValue).
using Azure.Messaging.ServiceBus;
private static async Task<List<ServiceBusReceivedMessage>> PeekAllMessages(string serviceBusConnectionString, string queueName)
{
var client = new ServiceBusClient(serviceBusConnectionString);
var receiver = client.CreateReceiver(queueName);
var messages = new List<ServiceBusReceivedMessage>();
var batchSize = 20;
var sequenceNumber = 0L;
do
{
var messageBatch = await receiver.PeekMessagesAsync(batchSize, sequenceNumber);
if (messageBatch.Count <= 0)
{
break;
}
// Increasing the SequenceNumber by 1 to avoid getting the message with the same SequenceNumber twice
sequenceNumber = messageBatch[^1].SequenceNumber + 1;
messages.AddRange(messageBatch);
} while (true);
return messages;
}
Since I am only allowed 10 event hubs per namespace, I am looking for a good algorithm to create a new namespace for every ten event hubs in the list I have.
NOTE: The list of event hub namespaces are being passed in to the method.
var eventHubResources = GetRequiredService<List<EventHubResource>>();
foreach (var eventHubResource in eventHubResources)
{
eventHubResource.ResourceGroup = resourceGroup;
MyNamespace.IEventHub eventHub = new EventHub(logger);
if (eventHubResource.CaptureSettings == null)
{
if (eventHubResources.IndexOf(eventHubResource) <= 9)
{
await eventHub.CreateEventHubAsync(azure, eventHubNamespace[0], eventHubResource, null);
}
if ((eventHubResources.IndexOf(eventHubResource) > 9) && (eventHubResources.IndexOf(eventHubResource) <= 19))
{
await eventHub.CreateEventHubAsync(azure, eventHubNamespace[1], eventHubResource, null);
}
// and so on....
}
else
{
await eventHub.CreateEventHubAsync(azure, eventHubNamespace, eventHubResource, storageAccount);
}
}
What is a better way to achieve this, compared to what I have above?
I didn't test the code but you can get the idea.
public static async Task CreateNamespaces(List<string> eventhubNames, ServiceClientCredentials creds) {
int totalEventHubsInNamespace = 0;
var ehClient = new EventHubManagementClient(creds)
{
SubscriptionId = "<my subscription id>"
};
foreach (var ehName in eventhubNames)
{
if (totalEventHubsInNamespace == 0)
{
var namespaceParams = new EHNamespace()
{
Location = "<datacenter location>"
};
// Create namespace
var namespaceName = "<populate some unique namespace>";
Console.WriteLine($"Creating namespace... {namespaceName}");
await ehClient.Namespaces.CreateOrUpdateAsync(resourceGroupName, namespaceName, namespaceParams);
Console.WriteLine("Created namespace successfully.");
}
// Create eventhub.
Console.WriteLine($"Creating eventhub {ehName}");
var ehParams = new Eventhub() { }; // Customize you eventhub here if you need.
await ehClient.EventHubs.CreateOrUpdateAsync(resourceGroupName, namespaceName, ehName, ehParams);
Console.WriteLine("Created Event Hub successfully.");
totalEventHubsInNamespace++;
if (totalEventHubsInNamespace >= 10)
{
totalEventHubsInNamespace = 0;
}
}
}
I implemented a simple IoT Edge module from scratch as simulated temperature and humidity sensor.
When I try to see the messages being sent to the cloud, I receive this error:
SendEventAsync for a named output is available for Modules only.
This is my code:
private static async Task SendMessages(DeviceClient deviceClient)
{
Console.WriteLine("Device sending {0} messages to IoTHub...\n", MessageCount);
var rnd = new Random();
for (var count = 0; count < MessageCount; count++)
{
_temperature = rnd.Next(20, 35);
_humidity = rnd.Next(60, 80);
var alert = _temperature > TemperatureThreshold ? "true" : "false";
var messageBody = new MessageBody
{
DeviceId = ModuleId,
MessageId = count,
Temperature = _temperature,
Humidity = _humidity
};
var dataBuffer = JsonConvert.SerializeObject(messageBody);
var eventMessage = new Message(Encoding.UTF8.GetBytes(dataBuffer));
eventMessage.Properties.Add("temperatureAlert", alert);
Console.WriteLine("\t{0}> Sending message: {1}, Data: [{2}]", DateTime.Now.ToLocalTime(), count, dataBuffer);
await deviceClient.SendEventAsync("toFilterModule", eventMessage);
}
}
This is my cinnection string:
var connectionString = Environment.GetEnvironmentVariable("EdgeHubConnectionString");
Any idea of how to fix it?
Thank you!
I am trying to write some c# script to write some data to celery queue that has been already created by my celery app (which I developed in Python). Whenever I try to send data to the celery queue the messages stuck under "Unacked" status, which doesn't happens whenever I write those same messages to another queue.
Here is my c# code that tries to write the messages to the celery queue under rabbitmq:
public static void SendMessages(List < CeleryMessage > celeryMessages, string taskName) {
ConnectionFactory factory = new ConnectionFactory {
HostName = CeleryHostName,
UserName = "guest",
Password = "guest",
VirtualHost = "/"
};
using(IConnection connection = factory.CreateConnection())
using(IModel channel = connection.CreateModel()) {
foreach(CeleryMessage celeryMessage in celeryMessages) {
SendMessageSecondVersion(channel, celeryMessage, taskName);
Console.WriteLine(" [X] Sent {0}", celeryMessage);
}
}
}
private static void SendMessageSecondVersion(IModel channel, CeleryMessage celeryMessage, string taskName) {
var myDict = new Dictionary < string,
string > ();
myDict["id"] = "4cc7438e-afd4-4f8f-a2f3-f46567e7ca77";
myDict["task"] = "celery.task.PingTask";
myDict["args"] = "[]";
myDict["kwargs"] = "{}";
myDict["retries"] = "0";
myDict["eta"] = "2009-11-17T12:30:56.527191";
var result = string.Join(", ", myDict.Select(m => m.Key + ":" + m.Value).ToArray());
var body = Encoding.UTF8.GetBytes(result);
channel.BasicPublish(exchange: "testcelery", routingKey: "testcelery", basicProperties: null, body: body);
}