Azure Service Bus SendBatchAsync在遗留Web API中超时问题求助
问题
部署在Azure App Services上的遗留Web API,使用WindowsAzure.ServiceBus 7.0.1 NuGet包操作Azure Service Bus。调用PublishMessageBatch批量推送消息至队列时,每次都超时,无消息入队。认证采用AzureActiveDirectoryTokenProvider.AuthenticationCallback。
已尝试的操作:
- 添加
ConfigureAwait(false)排查死锁问题,无效 - 日志分析:Azure App Service日志流显示API调用返回HTTP 499状态码,客户端2分钟后自动取消请求
- 托管标识权限配置正确,除
SendBatchAsync外其他Service Bus操作正常
日志情况:推送到Kusto集群的最后一条日志为_logger.TraceInfo($"Value of audience: {audience}, authority: {authority} and state {state}");,后续无异常或错误日志输出。
需要解决两个问题:
- 如何修改代码让
PublishMessageBatch正常运行 - WindowsAzure.ServiceBus 7.0.1与Azure App Services是否存在已知超时问题
相关代码片段
public async Task PublishMessageBatch(IEnumerable<BrokeredMessage> queueMessages) { await _logger.ExecuteWithInstrumentationAsync("ServiceBusQueueClient.PublishMessageBatch", string.Empty, async () => { _logger.TraceInfo("Pushing message in Queue"); InitializeIfUnavailable(); await _sbQueueClient.SendBatchAsync(queueMessages).ConfigureAwait(false); return true; }); } public void InitializeIfUnavailable() { Uri serviceBusURI = _queueConnectionString.GetServiceBusURIFromConnectionString(); string aadAuthority = _configuration.GetValue<string>(CCMConstants.AADAuthorityURL); if (_sbQueueClient?.IsClosed != false) { lock (_lock) { if (_sbQueueClient?.IsClosed != false) { _logger.TraceInfo("initializing QueueClient AAD"); var azureAuthCallback = new AzureActiveDirectoryTokenProvider.AuthenticationCallback(GetTokenAsync); _sbQueueClient = QueueClient.CreateWithAzureActiveDirectory(serviceBusURI, _queuePath, azureAuthCallback, aadAuthority, ReceiveMode.PeekLock); _logger.TraceInfo("QueueClient initialized successfully with AAD"); _sbQueueClient.RetryPolicy = RetryPolicy.Default; } } } if (_sbNamespaceManager == null) { var azureAuthCallback = new AzureActiveDirectoryTokenProvider.AuthenticationCallback(GetTokenAsync); TokenProvider token = TokenProvider.CreateAzureActiveDirectoryTokenProvider(azureAuthCallback, serviceBusURI, aadAuthority); _sbNamespaceManager = new NamespaceManager(serviceBusURI, token); _logger.TraceInfo("Initialized NamespaceManager for ServiceBus with AAD"); } } private async Task<string> GetTokenAsync(string audience, string authority, object state) { _logger.TraceInfo($"Value of audience: {audience}, authority: {authority} and state {state}"); return (await (new ManagedIdentityCredential("<client-id>")).GetTokenAsync(new TokenRequestContext(new[] { "https://servicebus.azure.net/.default" }), CancellationToken.None)).Token; }
解决方案与分析
代码修改建议
- 修正令牌受众不匹配问题
WindowsAzure.ServiceBus 7.0.1的AAD认证回调中,audience参数是当前Service Bus命名空间的URI(如https://your-namespace.servicebus.windows.net/),但现有代码固定使用全局scopehttps://servicebus.azure.net/.default,导致令牌受众与Service Bus要求不匹配,被服务端拒绝后引发超时。修改GetTokenAsync方法:
private async Task<string> GetTokenAsync(string audience, string authority, object state) { _logger.TraceInfo($"Value of audience: {audience}, authority: {authority} and state {state}"); // 用传入的audience构造符合要求的scope var scope = $"{audience.TrimEnd('/')}/.default"; return (await (new ManagedIdentityCredential("<client-id>")).GetTokenAsync(new TokenRequestContext(new[] { scope }), CancellationToken.None)).Token; }
- 完善初始化逻辑的线程安全
现有NamespaceManager初始化未加锁,多线程环境下可能重复创建实例引发连接冲突,给该部分添加锁:
public void InitializeIfUnavailable() { Uri serviceBusURI = _queueConnectionString.GetServiceBusURIFromConnectionString(); string aadAuthority = _configuration.GetValue<string>(CCMConstants.AADAuthorityURL); if (_sbQueueClient?.IsClosed != false) { lock (_lock) { if (_sbQueueClient?.IsClosed != false) { _logger.TraceInfo("initializing QueueClient AAD"); var azureAuthCallback = new AzureActiveDirectoryTokenProvider.AuthenticationCallback(GetTokenAsync); _sbQueueClient = QueueClient.CreateWithAzureActiveDirectory(serviceBusURI, _queuePath, azureAuthCallback, aadAuthority, ReceiveMode.PeekLock); _logger.TraceInfo("QueueClient initialized successfully with AAD"); _sbQueueClient.RetryPolicy = RetryPolicy.Default; } } } if (_sbNamespaceManager == null) { lock (_lock) // 添加锁保证线程安全 { if (_sbNamespaceManager == null) { var azureAuthCallback = new AzureActiveDirectoryTokenProvider.AuthenticationCallback(GetTokenAsync); TokenProvider token = TokenProvider.CreateAzureActiveDirectoryTokenProvider(azureAuthCallback, serviceBusURI, aadAuthority); _sbNamespaceManager = new NamespaceManager(serviceBusURI, token); _logger.TraceInfo("Initialized NamespaceManager for ServiceBus with AAD"); } } } }
- 添加超时控制与取消令牌
给SendBatchAsync添加超时逻辑,避免无限等待,同时捕获超时异常便于排查:
public async Task PublishMessageBatch(IEnumerable<BrokeredMessage> queueMessages, CancellationToken cancellationToken = default) { await _logger.ExecuteWithInstrumentationAsync("ServiceBusQueueClient.PublishMessageBatch", string.Empty, async () => { _logger.TraceInfo("Pushing message in Queue"); InitializeIfUnavailable(); // 30秒超时,与传入的取消令牌关联 using var timeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30)); using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutTokenSource.Token); await _sbQueueClient.SendBatchAsync(queueMessages, linkedTokenSource.Token).ConfigureAwait(false); _logger.TraceInfo("Message batch sent successfully"); return true; }); }
WindowsAzure.ServiceBus 7.0.1与Azure App Services的已知问题
WindowsAzure.ServiceBus 7.0.1是2017年左右发布的老旧版本,存在以下与Azure App Services相关的兼容性问题:
- 该版本默认使用的TLS版本可能低于Azure App Services要求的最低标准(当前要求TLS 1.2+),导致连接被服务端拒绝,引发超时
- 旧版本的AAD认证实现与Azure App Services托管标识集成存在缺陷,令牌缓存、刷新逻辑不完善,可能导致认证流程卡住
- 客户端连接池管理逻辑存在漏洞,在Azure App Services多实例环境下易出现连接耗尽,引发超时
优先级建议:如果业务允许,优先升级到最新的Azure.Messaging.ServiceBus包,该包完全支持托管标识,性能、稳定性和兼容性远优于旧版本。
内容的提问来源于stack exchange,提问作者Pulkit Sharma
相关产品推荐
相关产品推荐

