问题
The challenge here is that I'm trying to create a single facade for dealing with queues and topics while maintaining semantics of Send
vs. Publish
For example:
public interface IServiceBus
{
Task Send<T>(T message, string destination, SendOptions options = null);
Task Publish<T>(T message, string topic, SendOptions options = null);
}
Send()
would send a message to a queue and Publish()
would publish a message to a topic. So I would need to have an instance of IQueueClient
and ITopicClient
to make these happen; I would inject these to my IServiceBus
implementation as dependencies and call them accordingly.
The problem is that the QueueClient and TopicClients require that you specify their destinations when you're newing up the clients which prevents me from allowing that as parameters for my IServiceBus
implementation.
I could just create a client on at the time the message is being created, but that's super inefficient. I looked around for at least a connection manager that could act as a factory for the clients but MessagingFactory
doesn't seem to be in this SDK (Microsoft.Azure.ServiceBus 3.4.0).
So the questions are - Is there some sort of factory out there that I can use that would allow me to create the proper clients on demand with the same efficiency one would gain by reusing the clients? - Is there some sort of override or alternative client object I should be using to achieve this effect? These two clients are really limiting.
回答1:
Since I believe we can assume that instances of QueueClient and TopicClient are thread safe, what you can do is register the resolved IServiceBus
concrete class as a singleton in your IoC container.
In the concrete ServiceBus, you can create a cache of previously seen topic and queue clients:
private readonly ConcurrentDictionary<string, ITopicClient> _topicClientCache
= new ConcurrentDictionary<string, ITopicClient>();
private readonly ConcurrentDictionary<string, IQueueClient> _queueClientCache
= new ConcurrentDictionary<string, IQueueClient>();
Then in your Publish
method
public async Task Publish<T>(T message, string destination, ...)
{
// i.e. destination is the topic
if (!_topicClientCache.TryGetValue(destination, out var topicClient))
{
topicClient = new TopicClient(_myServiceBusConnectionString, destination);
_topicClientCache.TryAdd(destination, topicClient);
}
... create and serialize message into azureMessage here
await topicClient.SendAsync(azureMessage);
}
And the same applies for your Send
implementation - it would check against the _queueClientCache
for the destination (queue name), and create it and cache it the first time it sees it.
回答2:
I was finally able to come across someone that had a similar issue. It turns out that they removed the MessagingFactory, but made the connection reusable. Each client has a constructor overload that takes the connection, so I registered the connection as a singleton and inject that instead of the client, and then just create the clients on demand.
see: https://github.com/Azure/azure-service-bus-dotnet/issues/556
My solution looked a little like this (full implementation omitted for brevity)
public class AzureServiceBus : IServiceBus
{
public AzureServiceBus(ServiceBusConnection connection, string replyTo)
{
_connection = connection;
_replyTo = replyTo;
_retryPolicy = new RetryExponential(
TimeSpan.FromSeconds(1),
TimeSpan.FromMinutes(1),
10);
}
public async Task Send<T>(T message, string destination)
{
var client = new QueueClient(_connection, destination, ReceiveMode.PeekLock, _retryPolicy);
// ... do work
}
public async Task Publish<T>(T message, string topic, SendOptions options = null)
{
var client = new TopicClient(_connection, topic, _retryPolicy);
// ... do work
}
}
来源:https://stackoverflow.com/questions/55546270/managing-multiple-queue-topic-clients