What is alternate of ServiceBus MessagingFactory in .Net Core? - azure

While converting my project from .Net framework 4.7 to .Net core 2.1, I'm facing issue with Servicebus MessagingFactory. I don't see any MessagingFactory class in new nuget package Microsoft.Azure.ServiceBus for .Net core.
My .Net framework 4.7 Code
private static readonly string messagingConnectionString = Environment.GetEnvironmentVariable("ServiceBusConnection");
private static Lazy<MessagingFactory> lazyMessagingFactory = new Lazy<MessagingFactory>(() =>
{
return MessagingFactory.CreateFromConnectionString(messagingConnectionString);
});
public static MessagingFactory MessagingFactory
{
get
{
return lazyMessagingFactory.Value;
}
}
public static MessagingFactory EventHubMessageFactory
{
get
{
return lazyEventhubMessagingFactory.Value;
}
}
public async Task SendMessageToQueueAsync(string queueName, string message)
{
QueueClient queueClient = MessagingFactory.CreateQueueClient(queueName);
BrokeredMessage brokeredMessage = new BrokeredMessage(new MemoryStream(Encoding.UTF8.GetBytes(message)), true);
await queueClient.SendAsync(brokeredMessage);
}
It was a best practices for high performance application, Also I have many queues under single service bus namespace and I push message based on configuration. I don't want to create QueueClient object in every request and don't want to maintain connection string for every queue.
What is alternate of MessagingFactory in .Net Core?

There are major changes when you are migrating .NetFramework code into .Netcore, you can see Guide for migrating to Azure.Messaging.ServiceBus from Microsoft.Azure.ServiceBus
Example below
static void Main(string[] args)
{
string connectionString = "<connection_string>";
string queueName = "<queue_name>";
// since ServiceBusClient implements IAsyncDisposable we create it with "await using"
var client = new ServiceBusClient(connectionString);
// create the sender
ServiceBusSender sender = client.CreateSender(queueName);
for (int i = 0; i < 10; i++)
{
// create a message that we can send. UTF-8 encoding is used when providing a string.
ServiceBusMessage message = new ServiceBusMessage($"Hello world {i}!");
// send the message
sender.SendMessageAsync(message).GetAwaiter().GetResult();
}
sender.DisposeAsync().GetAwaiter().GetResult();
client.DisposeAsync().GetAwaiter().GetResult();
}

https://github.com/MicrosoftDocs/azure-docs/issues/46830
https://github.com/Azure/azure-service-bus-dotnet/issues/556
While MessagingFactory is gone, the idea of connection pooling and sharing connections is there. When you create your clients, passing a connection will reuse it. When passing a connection string, will cause clients to establish a new connection.
So you can manually create a ServiceBusConnection or reuse one of an existing client. You can pass the connection object in the constructors of the clients you create. Take care not to close a connection accidentally, e.g. by closing the client that created it.

Related

Application Insights + Azure Service Bus - Capture message from the bus in dependency tracking

I am looking into how the app insights work with different types of dependencies. I have a question on using the AppInsights ASP Net Core sdk with Messaging Service Bus sdk.
How can I capture messages to service bus while sending or receiving using this sdk in dependency? I understand that this is not something we would like to log all the time and I will make it configurable.
Thanks
You can create your own sender and implement the desired logic inside the SendMessageAsync method:
public class TelemetryEnabledServiceBusSender : ServiceBusSender
{
private readonly TelemetryClient telemetryClient;
internal TelemetryEnabledServiceBusSender(ServiceBusClient client, string topic, TelemetryClient telemetryClient) : base(client, topic)
{
this.telemetryClient = telemetryClient;
}
public override async Task SendMessageAsync(ServiceBusMessage message, CancellationToken cancellationToken = default)
{
telemetryClient.TrackTrace(message.Body.ToString());
await base.SendMessageAsync(message, cancellationToken);
}
}
use it like this:
var serviceBusSender = new TelemetryEnabledServiceBusSender(serviceBusClient, serviceBusData.Topic, telemetryClient);
await serviceBusSender.SendMessageAsync(message);
Logging processed messages is even simpler and can be done using the ServiceBusProcessor
serviceBusProcessor.ProcessMessageAsync += ProcessMessageAsync;
...
private async Task ProcessMessageAsync(ProcessMessageEventArgs arg)
{
telemetryClient.TrackTrace(arg.Message.Body.ToString());
...
}
Adding my approach as an answer here as it is too long to comment.
var telemetry = new DependencyTelemetry(
"Azure Service Bus",
_serviceBusClient.FullyQualifiedNamespace,
"ServiceBusReceiver.Receive",
string.Empty
);
using var operation =
_telemetryClient.StartOperation(telemetry);
try
{
var receivedMessage = await _serviceBusReceiver.ReceiveMessageAsync();
if (receivedMessage?.Body is not null)
{
message = receivedMessage.Body.ToString();
telemetry.Data = message;
// do something
}
telemetry.Success = true;
//Consider set telemetry duration
}
catch (Exception e)
{
// log exception
_telemetryClient.TrackException(e);
telemetry.Success = false;
throw;
}
finally
{
_telemetryClient.TrackTrace("Done");
_telemetryClient.StopOperation(operation);
}
Thanks to #Peter Bons

Issue (maybe) with the code for putting message on the queue

I have this code to put message on the queue
public class AzureQueueService : IAzureQueueService
{
private readonly IConfiguration _config;
private static string azureServiceBusString = null;
public AzureQueueService(IConfiguration config)
{
_config = config;
azureServiceBusString = "connString";
}
public async Task SendMessageAsync<T>(T serviceBusMessage, string queueName)
{
// since ServiceBusClient implements IAsyncDisposable we create it with "await using"
await using var queueClient = new ServiceBusClient(azureServiceBusString);
// create the sender
ServiceBusSender sender = queueClient.CreateSender(queueName);
string messageBody = JsonSerializer.Serialize(serviceBusMessage);
var message = new ServiceBusMessage(Encoding.UTF8.GetBytes(messageBody));
// send the message
await sender.SendMessageAsync(message);
}
}
In my startup file I am doing this
services.AddTransient<IAzureQueueService, Core.Services.AzureQueueService>();
And I am using it like this
var queue = new AzureQueueService(config);
await queue.SendMessageAsync(message, "emailqueue");
Could this lead to memory leakage? I mean should I instantiate the ServiceBusClient in the constructor?
Yes, I'd be looking to set up dependency injection for the ServiceBusClient. As per the docs here (for 7.8.x)
The ServiceBusClient, senders, receivers, and processors are safe to cache and use as a singleton for the lifetime of the application, which is best practice when messages are being sent or received regularly. They are responsible for efficient management of network, CPU, and memory use, working to keep usage low during periods of inactivity.
Also, see Best Practises for performance improvements using Service Bus Messaging

Migration from Microsoft.Azure.ServiceBus to Azure.Messaging.ServiceBus

I'm trying to update this function to make use of Azure.Messaging.ServiceBus and drop Microsoft.Azure.ServiceBus altogether, however cant seem to find any resources for this. Anybody knows how to send a message to a topic using this package?
The older function is:
public async Task SendMessageToServiceBusTopic<T>(T request, string topicSubName, string submissionNumber)
{
ServiceBusConnectionStringBuilder serviceBusConnectionStringBuilder =
new ServiceBusConnectionStringBuilder(settings.ServiceBusConnectionString)
{
EntityPath = settings.ServiceBusTopic
};
TopicClient topicClient = new TopicClient(serviceBusConnectionStringBuilder);
byte[] bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(request));
await topicClient.SendAsync(new Message(bytes)
{
CorrelationId = context.CorrelationId,
Label=topicSubName,
UserProperties = { new KeyValuePair<string, object>("TrackingId", submissionNumber) }
});
}
So far I have managed:
Am i headed in the right direction?
public async Task SendMessageToServiceBusTopic<T>(T request, string topicSubName, string submissionNumber)
{
ServiceBusClient client = new ServiceBusClient(settings.ServiceBusConnectionString);
ServiceBusSender s = client.CreateSender(settings.ServiceBusTopic);
byte[] bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(request));
await s.SendMessageAsync(new ServiceBusMessage(bytes));
}
While you can construct a Service Bus Client each time, it's not ideal. Assuming you're using the latest In-Proc SDK, you can use one of these options:
[FunctionName("PublishToTopic")]
public static async Task Run(
[TimerTrigger("0 */5 * * * *")] TimerInfo myTimer,
[ServiceBus("<topic_name>", Connection = "<connection_name>")] IAsyncCollector<ServiceBusMessage> collector)
{
await collector.AddAsync(new ServiceBusMessage(new BinaryData($"Message 1 added at: {DateTime.Now}")));
await collector.AddAsync(new ServiceBusMessage(new BinaryData($"Message 2 added at: {DateTime.Now}")));
}
Alternatively,
[FunctionName("PublishWithSender"]
public static async Task Run(
[TimerTrigger("0 */5 * * * *")] TimerInfo myTimer,
[ServiceBus("<topic_name>", Connection = "<connection_name>")] ServiceBusSender sender)
{
await sender.SendMessagesAsync(new[]
{
new ServiceBusMessage(new BinaryData($"Message 1 added at: {DateTime.Now}")),
new ServiceBusMessage(new BinaryData($"Message 2 added at: {DateTime.Now}"))
});
}
For Isolated Worker SDK it's somewhat different. See this post for details.
you're in the right direction. There's no easy migration tool/sample as they are different libraries dealing with the same service (Azure Service Bus).
I was in the same situation like you (trying to migrate to Azure.Messaging.ServiceBus which is now the recommended NuGet from Microsoft).
You can have a look at the migration guide from their Github to have an idea of how to make the transition smoother:
https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/servicebus/Azure.Messaging.ServiceBus/MigrationGuide.md
/!\ IQueueClient and ITopicClient (both coming from Microsoft.Azure.ServiceBus) have been merged into a single object called ServiceBusSender.
Why ? to make our life easier ! Indeed, under the hood, ServiceBusSender now takes care of the process of sending the message, especially since a queue and a topic cannot have the same name if I'm not wrong.
Here's a sample code of mine to send a message using the new library:
/// <summary>
/// Send a message by serializing as JSON the object in input.
/// </summary>
/// <param name="queueOrTopicName">Name of the queue or topic where the message will be sent.</param>
/// <param name="messageToSend">Any C# object</param>
/// <returns></returns>
public async Task SendMessage(string queueOrTopicName, object messageToSend)
{
//ServiceBusSender should not be disposed (according to the documentation, Github, etc.)
using ServiceBusClient client = new ServiceBusClient(connectionString: "Your-ServiceBus-ConnectionString");
using ServiceBusSender sender = client.CreateSender(_queueOrTopicName);
// create a message that we can send. UTF-8 encoding is used when providing a string.
ServiceBusMessage message = BuildServiceBusMessage(messageToSend);
// Finally send the message
await sender.SendMessageAsync(message);
}
private ServiceBusMessage BuildServiceBusMessage<T>(T entity)
{
string serializedMessage = JsonConvert.SerializeObject(entity); // Still using Newtonsoft.Json but I don't see any obstacles of using System.Text.Json.
ServiceBusMessage message = new ServiceBusMessage(serializedMessage)
{
MessageId = Guid.NewGuid().ToString(),
ContentType = "application/json; charset=utf-8",
};
return message;
}
If you have any needs to do Dependency Injection (to re-use the same ServiceBusClient object and avoiding instanciating ServiceBusClient for each message you want to send for example), you can refer to this stackoverflow that I discovered this week
How to register ServiceBusClient for dependency injection?

Azure Service Bus multiple QueueClients

What is the best practice for creating multiple queueclients for listening to different service bus queues? There is a MessagingFactory class however Microsoft.ServiceBus.Messaging not seems to be available as a nuget package anymore (.net core console application).
Considering QueueClient as static object what would be the recommended pattern to create multiple queueclients from a singleton host process?
Appreciate the feedback.
For .net core applications, you can make use of Microsoft.Azure.ServiceBus instead of Microsoft.ServiceBus.Messaging nuget. As this is build over .net standard, this can be used in both framework and core applications. Methods and classes similar to Microsoft.ServiceBus.Messaging are available under this. Check here for samples.
Able to get it working however could not use dependency injection. Any suggestions on improving this implementation would be much appreciated.
Startup.cs
// Hosted services
services.AddSingleton();
ServiceBusListener.cs
public class ServiceBusListener : BackgroundService, IServiceBusListener
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
Console.WriteLine($"ServiceBusListener is starting.");
Dictionary<string, QueueClient> queueClients = new Dictionary<string, QueueClient>();
foreach (var queue in _svcBusSettings.Queues)
{
var svcBusQueueClient = new ServiceBusQueueClient(queue.Value, queue.Key);
queueClients.Add(queue.Key, svcBusQueueClient.QueueClient);
}
}
}
ServiceBusQueueClient.cs
public class ServiceBusQueueClient : IServiceBusQueueClient
{
private IQueueClient _queueClient;
public QueueClient QueueClient
{
get { return _queueClient as QueueClient; }
}
public ServiceBusQueueClient(string serviceBusConnection, string queueName)
{
_queueClient = new QueueClient(serviceBusConnection, queueName);
RegisterOnMessageHandlerAndReceiveMessages();
}
}

How to specify EventHub Consumer Group in a WebJob?

I am using WebJob's bindings to EventHub as described here:
https://github.com/Azure/azure-webjobs-sdk/wiki/EventHub-support
While the webjob is running, trying to run the Azure Service Bus Explorer on the same hub result in this exception:
Exception: A receiver with a higher epoch '14' already exists. A new receiver with epoch 0 cannot be created.
Make sure you are creating receiver with increasing epoch value to ensure connectivity, or ensure all old epoch receivers are closed or disconnected.
From what I understand, this is caused by the 2 listeners(webjob & bus explorer) using the same Consumer Group.
So my question, how can I specify a different Consumer Group in my webjob ?
My current code look like this:
Program.cs:
var config = new JobHostConfiguration()
{
NameResolver = new NameResolver()
};
string eventHubConnectionString = ConfigurationManager.ConnectionStrings["EventHub"].ConnectionString;
string eventHubName = ConfigurationManager.AppSettings["EventHubName"];
string eventProcessorHostStorageConnectionString = ConfigurationManager.ConnectionStrings["EventProcessorHostStorage"].ConnectionString; ;
var eventHubConfig = new EventHubConfiguration();
eventHubConfig.AddReceiver(eventHubName, eventHubConnectionString, eventProcessorHostStorageConnectionString);
config.UseEventHub(eventHubConfig);
var host = new JobHost(config);
host.RunAndBlock();
Functions.cs:
public class Functions
{
public static void Trigger([EventHubTrigger("%EventHubName%")] string message, TextWriter log)
{
log.WriteLine(message);
}
}
[Edit - Bonus Question]
I don't fully grasp the use of Consumer Group and 'epoch' thing. One Consumer Group is limited to one receiver ?
The EventHubTrigger has an optional ConsumerGroup property (source). So, based on that modify the trigger like this:
public class Functions
{
public static void Trigger([EventHubTrigger("%EventHubName%", ConsumerGroup = "MyConsumerGroup")] string message, TextWriter log)
{
log.WriteLine(message);
}
}

Resources