.NET 8升级后Service Bus消息复制异常及重试失效问题求助
.NET 8升级后Service Bus消息重发丢失自定义属性及重试计数器失效的解决方案
问题分析
升级到.NET 8后,原有的Service Bus消息重发逻辑出现两个问题:
- 使用
new ServiceBusMessage(message)复制消息时,自定义属性未被完整复制,导致后续重试时交易编号丢失 - 手动发送新消息的方式无法延续原消息的
DeliveryCount,重试计数器自动递增失效
原因
Azure.Messaging.ServiceBus在适配.NET 8的版本中,ServiceBusMessage的复制构造函数行为发生了变更,不再默认复制所有自定义属性;同时,重新发送的新消息属于全新投递,自然不会继承原消息的投递计数。
解决方案
1. 手动完整复制自定义属性
放弃依赖复制构造函数,手动复制原消息的所有自定义属性和必要系统属性到新消息中:
// 基于原消息体创建新消息 var newMessage = new ServiceBusMessage(message.Body) { ScheduledEnqueueTime = DateTime.UtcNow.AddMinutes(_config.HoldServiceBusMsgMinutes), // 按需复制系统属性,比如消息ID、关联ID MessageId = message.MessageId, CorrelationId = message.CorrelationId }; // 遍历复制所有自定义属性 foreach (var prop in message.ApplicationProperties) { newMessage.ApplicationProperties[prop.Key] = prop.Value; }
2. 维护自定义重试计数器
由于新消息无法继承原DeliveryCount,需要在自定义属性中维护专属的重试计数:
// 读取当前重试计数:首次重试用原消息DeliveryCount,后续从自定义属性读取 int currentRetryCount; if (message.ApplicationProperties.TryGetValue("RetryCount", out var retryVal) && int.TryParse(retryVal.ToString(), out currentRetryCount)) { currentRetryCount++; } else { currentRetryCount = message.DeliveryCount; } // 将更新后的计数存入新消息的自定义属性 newMessage.ApplicationProperties["RetryCount"] = currentRetryCount;
优化后的完整代码
catch (Exception ex) when (ex.Message.Contains("Not Found")) { var parsedMessage = _messageReceiver.ParseServiceBusMessage(message); // 获取当前重试计数 int currentRetryCount; if (message.ApplicationProperties.TryGetValue("RetryCount", out var retryVal) && int.TryParse(retryVal.ToString(), out currentRetryCount)) { currentRetryCount++; } else { currentRetryCount = message.DeliveryCount; } if (currentRetryCount <= _config.MaxRetryCount) { using (var ts = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) { await _messageReceiver.CompleteMessageAsync(message, messageReceiver); // 手动构建新消息并复制所有必要属性 var newMessage = new ServiceBusMessage(message.Body) { ScheduledEnqueueTime = DateTime.UtcNow.AddMinutes(_config.HoldServiceBusMsgMinutes), MessageId = message.MessageId, CorrelationId = message.CorrelationId }; // 复制原消息自定义属性 foreach (var prop in message.ApplicationProperties) { newMessage.ApplicationProperties[prop.Key] = prop.Value; } // 更新自定义重试计数 newMessage.ApplicationProperties["RetryCount"] = currentRetryCount; await _serviceBusClient.SendAsync(newMessage); _logger.LogInformation("Retry counter: {retryCount} Processing order number {transactionNumber} resulted in not found condition. Resubmitted to service bus", currentRetryCount, parsedMessage.Transaction); ts.Complete(); } } else { _logger.LogError("Max retry count reached for transaction number {transactionNumber}", parsedMessage.Transaction); await _messageReceiver.DeadletterMessageAsync(message, messageReceiver, ex); } }
额外提示
- 如果不需要高度自定义的重试逻辑,可以考虑使用Service Bus内置的重试策略,结合消息的
ScheduledEnqueueTime配置延迟,减少手动维护成本 - 确保Azure.Messaging.ServiceBus NuGet包更新到适配.NET 8的最新稳定版本,避免版本兼容问题
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

