当C# ServiceBusClient无法连接Service Bus时如何退出应用
我的.NET 7 Worker Service应用无法连接Service Bus实例时,日志已出现错误:
[ERR] ErrorHandler invoked with ErrorSource: Receive, FullyQualifiedNamespace: EntityPath, Exception: Azure.Messaging.ServiceBus.ServiceBusException: The requested name is valid, but no data o...
但异常未抛入业务代码,应用仅无限重试而不退出:
[WRN] RunOperation encountered an exception and will retry. Exception: Azure.Messaging.ServiceBus.ServiceBusException: The requested name is valid, but no data of the requested type was found. ErrorCode: NoData (ServiceCommunicationProblem).
无法定位这些日志的来源,也无法拦截该异常(推测来自Azure.Messaging.ServiceBus)。需求是:当无法连接ServiceBus或出现上述场景时,让应用抛出异常并退出。
实现细节
program.cs中Worker与ServiceBusClient配置
services.AddHostedService<ServiceBusWorker>(); services.AddAzureClients(clientsBuilder => { clientsBuilder.AddServiceBusClient(serviceBusConfiguration?.ConnectionString) .WithName(serviceBusConfiguration?.MyQueue); });
Worker构造函数
using Azure.Messaging.ServiceBus; public ServiceBusWorker(IAzureClientFactory<ServiceBusClient> serviceBusClientFactory) { _serviceBusClientFactory = serviceBusClientFactory; var connectionString = serviceBusConfiguration.Value.ConnectionString; _requestQueueClient = _serviceBusClientFactory.CreateClient(_requestQueue); }
Worker启动逻辑
public async Task StartAsync(CancellationToken cancellationToken) { try { await using var requestProcessor = _requestQueueClient.CreateProcessor(_requestQueue, _serviceBusProcessorOptions); // 配置消息和错误处理程序 requestProcessor.ProcessMessageAsync += ProcessMessageAsync; requestProcessor.ProcessErrorAsync += ErrorHandler; await requestProcessor.StartProcessingAsync(cancellationToken); while (!cancellationToken.IsCancellationRequested) { await Task.Delay(1000, cancellationToken); } // 临时方案,保持processor存活,后续优化 } catch (Exception ex) { _logger.LogError(ex, "启动请求处理器时出错"); throw; } }
已尝试的操作
在program.cs中配置重试策略,但无效果,推测该策略针对消息处理重试,而非连接失败场景:
services.AddAzureClients(clientsBuilder => { var serviceBusRetryOptions = new ServiceBusRetryOptions { MaxRetries = 3, Delay = TimeSpan.FromSeconds(2), TryTimeout = TimeSpan.FromSeconds(30) }; clientsBuilder .AddServiceBusClient(serviceBusConfiguration?.ConnectionString) .ConfigureOptions(options => { options.RetryOptions = serviceBusRetryOptions; }) .WithName(serviceBusConfiguration?.RequestQueue); clientsBuilder .AddServiceBusClient(serviceBusConfiguration?.ConnectionString) .ConfigureOptions(options => { options.RetryOptions = serviceBusRetryOptions; }) .WithName(serviceBusConfiguration?.ResponseQueue); });
解决方案
1. 拦截ProcessErrorAsync事件判断致命错误
Service Bus Processor的ProcessErrorAsync会捕获连接类错误,可在此判断错误类型,若为无法恢复的连接错误则触发应用退出。
修改ErrorHandler方法:
private readonly IHostApplicationLifetime _hostApplicationLifetime; // 构造函数注入IHostApplicationLifetime public ServiceBusWorker(IAzureClientFactory<ServiceBusClient> serviceBusClientFactory, IHostApplicationLifetime hostApplicationLifetime) { _serviceBusClientFactory = serviceBusClientFactory; _hostApplicationLifetime = hostApplicationLifetime; var connectionString = serviceBusConfiguration.Value.ConnectionString; _requestQueueClient = _serviceBusClientFactory.CreateClient(_requestQueue); } private async Task ErrorHandler(ProcessErrorEventArgs args) { _logger.LogError(args.Exception, "Service Bus处理错误,来源:{ErrorSource}", args.ErrorSource); // 判断是否为无法恢复的连接错误 if (args.Exception is ServiceBusException sbEx && sbEx.Reason == ServiceBusFailureReason.ServiceCommunicationProblem) { _logger.LogCritical("无法连接到Service Bus,应用将退出"); // 触发主机停止,让Worker Service退出 await _hostApplicationLifetime.StopApplicationAsync(); return; } // 其他错误按需处理 }
2. 启动阶段主动验证连接
在启动时主动发起连接验证,而非依赖Processor自动重试,失败时直接触发退出:
public async Task StartAsync(CancellationToken cancellationToken) { try { // 主动验证Service Bus连接 await _requestQueueClient.GetQueueRuntimePropertiesAsync(_requestQueue, cancellationToken); await using var requestProcessor = _requestQueueClient.CreateProcessor(_requestQueue, _serviceBusProcessorOptions); requestProcessor.ProcessMessageAsync += ProcessMessageAsync; requestProcessor.ProcessErrorAsync += ErrorHandler; await requestProcessor.StartProcessingAsync(cancellationToken); while (!cancellationToken.IsCancellationRequested) { await Task.Delay(1000, cancellationToken); } } catch (Exception ex) { _logger.LogCritical(ex, "启动失败,无法连接到Service Bus"); await _hostApplicationLifetime.StopApplicationAsync(); throw; } }
GetQueueRuntimePropertiesAsync会触发实际连接请求,失败时直接抛出异常,此时可捕获并触发应用退出。
3. 关闭Processor自动重试(可选)
若希望Processor遇到连接错误后不再重试,可在创建Processor时配置相关参数:
var _serviceBusProcessorOptions = new ServiceBusProcessorOptions { // 关闭自动锁续期 MaxAutoLockRenewalDuration = TimeSpan.Zero, // 按需设置其他限制 };
更推荐通过ProcessErrorAsync区分临时错误与致命错误后主动退出,避免误杀可恢复的场景。
内容的提问来源于stack exchange,提问作者fourbeatcoder

