【问题标题】:Azure Service Bus not all messages received in hosted service web appAzure 服务总线并非托管服务 Web 应用中收到的所有消息
【发布时间】:2022-11-10 23:44:55
【问题描述】:

在 .net Web 应用程序中,我设置了一个托管服务来接收来自 Azure 服务总线主题的消息。问题是不是所有的消息都被接收到,只接收到任意数量的消息(例如,20 条消息只接收到 12 条)。其余的都进入了死信队列。当消息同时发送时会发生这种情况。 我尝试了以下步骤来解决这个问题:

  1. 增加了最大并发调用量,这有帮助,但不能保证
  2. 添加了预取计数

    我还尝试通过 Azure 服务总线资源中的功能发送消息。 500 条消息,没有间隔时间 --> 不起作用(对于所有消息)。 500条消息,1s间隔时间,所有消息都收到。

    我只是不明白为什么接收者没有收到所有的消息。 我想构建一个事件驱动的架构,如果所有消息都被处理,我不能让它成为一场赌博。

    启动.cs

    ...
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<IServiceBusTopicSubscription,ServiceBusSubscription>();
        services.AddHostedService<WorkerServiceBus>();
    }
    ...
            
    

    工人服务.cs

    public class WorkerServiceBus : IHostedService, IDisposable
    {
        private readonly ILogger<WorkerServiceBus> _logger;
        private readonly IServiceBusTopicSubscription _serviceBusTopicSubscription;
    
    
    
    
        public WorkerServiceBus(IServiceBusTopicSubscription serviceBusTopicSubscription,
            ILogger<WorkerServiceBus> logger)
        {
            _serviceBusTopicSubscription = serviceBusTopicSubscription;
            _logger = logger;
        }
    
    
    
       public async Task StartAsync(CancellationToken stoppingToken)
        {
            _logger.LogInformation("Starting the service bus queue consumer and the subscription");
            await _serviceBusTopicSubscription.PrepareFiltersAndHandleMessages().ConfigureAwait(false);
        }
    
    
    
       public async Task StopAsync(CancellationToken stoppingToken)
        {
            _logger.LogInformation("Stopping the service bus queue consumer and the subscription");
            await _serviceBusTopicSubscription.CloseSubscriptionAsync().ConfigureAwait(false);
        }
    
    
    
       public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    
    
    
       protected virtual async void Dispose(bool disposing)
        {
            if (disposing)
            {
                await _serviceBusTopicSubscription.DisposeAsync().ConfigureAwait(false);
            }
        }
    }
    
    

    服务总线订阅.cs

    public class ServiceBusSubscription : IServiceBusTopicSubscription
    {
        private readonly IConfiguration _configuration;
        private const string TOPIC_PATH = "test";
        private const string SUBSCRIPTION_NAME = "test-subscriber";
        private readonly ILogger _logger;
        private readonly ServiceBusClient _client;
        private readonly IServiceScopeFactory _scopeFactory;
        private ServiceBusProcessor _processor;
    
    
    
       public ServiceBusBookingsSubscription(IConfiguration configuration,
            ILogger<ServiceBusBookingsSubscription> logger,
            IServiceScopeFactory scopeFactory)
        {
            _configuration = configuration;
            _logger = logger;
            _scopeFactory = scopeFactory;
    
    
    
           var connectionString = _configuration.GetConnectionString("ServiceBus");
            var serviceBusOptions = new ServiceBusClientOptions()
            {
                TransportType = ServiceBusTransportType.AmqpWebSockets
            };
            _client = new ServiceBusClient(connectionString, serviceBusOptions);
        }
    
    
    
       public async Task PrepareFiltersAndHandleMessages()
        {
            ServiceBusProcessorOptions _serviceBusProcessorOptions = new ServiceBusProcessorOptions
            {
                MaxConcurrentCalls = 200,
                AutoCompleteMessages = false,
                PrefetchCount = 1000,
            };
    
    
    
           _processor = _client.CreateProcessor(TOPIC_PATH, SUBSCRIPTION_NAME, _serviceBusProcessorOptions);
            _processor.ProcessMessageAsync += ProcessMessagesAsync;
            _processor.ProcessErrorAsync += ProcessErrorAsync;
    
    
    
           await _processor.StartProcessingAsync().ConfigureAwait(false);
        }
    
    
    
       private async Task ProcessMessagesAsync(ProcessMessageEventArgs args)
        {
            _logger.LogInformation($"Received message from service bus");
            _logger.LogInformation($"Message: {args.Message.Body}");
            var payload = args.Message.Body.ToObjectFromJson<List<SchedulerBookingViewModel>>();
    
    
    
           // Create scoped dbcontext
            using var scope = _scopeFactory.CreateScope();
            var dbContext = scope.ServiceProvider.GetRequiredService<dbContext>();
    
    
    
           // Process payload
            await new TestServiceBus().DoThings(payload);
    
    
    
           await args.CompleteMessageAsync(args.Message).ConfigureAwait(false);
        }
    
    
    
       private Task ProcessErrorAsync(ProcessErrorEventArgs arg)
        {
            _logger.LogError(arg.Exception, "Message handler encountered an exception");
            _logger.LogError($"- ErrorSource: {arg.ErrorSource}");
            _logger.LogError($"- Entity Path: {arg.EntityPath}");
            _logger.LogError($"- FullyQualifiedNamespace: {arg.FullyQualifiedNamespace}");
    
    
    
           return Task.CompletedTask;
        }
    
    
    
       public async ValueTask DisposeAsync()
        {
            if (_processor != null)
            {
                await _processor.DisposeAsync().ConfigureAwait(false);
            }
    
    
    
           if (_client != null)
            {
                await _client.DisposeAsync().ConfigureAwait(false);
            }
        }
    
    
    
       public async Task CloseSubscriptionAsync()
        {
            await _processor.CloseAsync().ConfigureAwait(false);
        }
    }
    

【问题讨论】:

  • 1. 死信消息,消息头提供的原因是什么? 2.您的订阅实体是如何配置的(交付计数、锁定持续时间)? 3. 你的处理程序TestServiceBus().DoThings(payload) 执行需要多长时间?
  • 1. deadLetterReason: MaxDeliveryCountExceeded, deadLetterErrorDescription: 3 次投递尝试后无法消费消息。 2. 我将传递计数设置为 3。锁定持续时间设置为 30 秒 当消息是死信时,Web 应用程序中不会记录错误。
  • 3.最短时间1s,平均5s,最长24s
  • 对于 ProcessMessagesAsync 中的逻辑,我会将其包装在 try/catch 中。记录任何捕获的异常,然后在最后throw; 它,以便重新处理消息。这使您有机会了解为什么消息无法处理。我们(和许多其他人)已经构建了稳定的应用程序,可以可靠地处理 Azure 服务总线上的消息。你也可以。

标签: asp.net azure azureservicebus azure-servicebus-topics asp.net-core-hosted-services


【解决方案1】:

所以这就是我们解决问题的方法。它与为门户中的 Azure 资源设置的消息锁定持续时间有关。以前的值:30 秒。新值:3 分钟。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多