我有两个不同的服务类实现,我们可以从 Web 应用程序中使用它们来与 Azure 服务总线进行通信。 他们都工作。
其中一个是我认为的标准单例服务类,它实例化和处置 ServiceBus 客户端和发送方,并在启动时使用 .net DI 添加为单例。
另一个是我最近使用 AzureAddClients DI 方法来配置 ServiceBus 客户端和发送器作为启动代码的一部分,而不是将该逻辑封装在服务类中。 服务类采用 IAzureCLientFactory 来在需要时触发发送者等的延迟实例化,并允许服务类在需要时确定作用域而不是单例(尽管感觉服务类仍然可以作为单例运行良好?)。
第二种方法似乎是最新的微软文档中推荐的方法,但当我编写第一个实现时,这两种方法都不存在(或者我错过了)。
一种方法相对于另一种方法是否有任何优势/最佳实践原因? 为了清楚起见,下面两种不同方法的代码。
启动代码
services.AddSingleton<IAIMarkingMessageService>(s => new AIMarkingMessageServiceSingleton(aiMarkingSettings.ServiceBusNamespace, aiMarkingSettings.InitQueueName, aiMarkingSettings.FeedbackQueueName, siteSettings.DefaultManagedIdentityClientId));
服务类别代码/大纲(为简洁起见,删除了一些细节)
public class AIMarkingMessageServiceSingleton : IAIMarkingMessageService, IAsyncDisposable
{
readonly ServiceBusClient _client;
readonly ServiceBusSender _initSender;
readonly ServiceBusSender _feedbackSender;
public AIMarkingMessageServiceSingleton(string serviceBusNameSpace, string initQueueName, string feedbackQueueName, string userAssignedClientId)
{
var clientOptions = new ServiceBusClientOptions { TransportType = ServiceBusTransportType.AmqpWebSockets };
_client = new ServiceBusClient($"{serviceBusNameSpace}.servicebus.windows.net", new DefaultAzureCredential(new DefaultAzureCredentialOptions { ManagedIdentityClientId = userAssignedClientId }), clientOptions);
_initSender = _client.CreateSender(initQueueName);
_feedbackSender = _client.CreateSender(feedbackQueueName);
}
public async Task<string> SendInitSubmissionAsync(long taskId, string requestUID)
{
...
}
public async Task<List<long>> SendInitSubmissionBatchAsync(IEnumerable<(long taskId, string requestUID)> tasksToInit)
{
...
}
public async ValueTask DisposeAsync()
{
await _client.DisposeAsync(); // senders and receivers created by this client will also be disposed by this call
GC.SuppressFinalize(this);
}
}
启动代码
services.AddAzureClients(clientBuilder =>
{
// register the clients
clientBuilder.AddServiceBusClientWithNamespace($"{aiMarkingSettings.ServiceBusNamespace}.servicebus.windows.net")
.ConfigureOptions(x => new ServiceBusClientOptions { TransportType = ServiceBusTransportType.AmqpTcp });
clientBuilder.UseCredential(new DefaultAzureCredential(new DefaultAzureCredentialOptions { ManagedIdentityClientId = siteSettings.DefaultManagedIdentityClientId }));
// register the sub clients (queue senders)
// init sender
clientBuilder.AddClient<ServiceBusSender, ServiceBusClientOptions>(
(_, _, provider) => provider.GetService<ServiceBusClient>()
.CreateSender(aiMarkingSettings.InitQueueName)).WithName(aiMarkingSettings.InitQueueName);
// feedback sender
clientBuilder.AddClient<ServiceBusSender, ServiceBusClientOptions>(
(_, _, provider) => provider.GetService<ServiceBusClient>()
.CreateSender(aiMarkingSettings.FeedbackQueueName)).WithName(aiMarkingSettings.FeedbackQueueName);
});
// could/should be singleton rather than scoped?
services.AddScoped<IAIMarkingMessageService, AIMarkingMessageService>();
服务类别代码/概要
public class AIMarkingMessageService : IAIMarkingMessageService
{
readonly ServiceBusSender _initSender;
readonly ServiceBusSender _feedbackSender;
public AIMarkingMessageService(IAzureClientFactory<ServiceBusSender> senderFactory, IOptions<AIMarkingSettings> config )
{
var _config = config.Value;
_initSender = senderFactory.CreateClient(_config.InitQueueName);
_feedbackSender = senderFactory.CreateClient(_config.FeedbackQueueName);
}
public async Task<string> SendInitSubmissionAsync(long taskId, string requestUID)
{
...
}
public async Task<List<long>> SendInitSubmissionBatchAsync(IEnumerable<(long taskId, string requestUID)> tasksToInit)
{
...
}
}
对于此实现,没有理由优先选择其中之一。这取决于对更自然地适合应用程序模式的偏好。
这两种方法是等效的,因为它们都处理重要部分:
有一个
ServiceBusClient
实例和每个可重复使用的发送者。
ServiceBusClient
的生命周期由DI管理并妥善处置。