Leaders Logo

Aplicação de boas práticas no desenvolvimento de workers em .NET para o consumo de mensagens no Azure Service Bus

Introdução ao Azure Service Bus

O Azure Service Bus é uma solução de mensageria oferecida pela Microsoft Azure que permite a comunicação entre diferentes componentes de uma aplicação de forma confiável e escalável (PIISPANEN, 2022). Ele suporta padrões de mensagens como filas e tópicos, possibilitando que as mensagens sejam enviadas e recebidas de forma assíncrona. Isso é especialmente útil em arquiteturas distribuídas, onde os diferentes componentes da aplicação podem estar em servidores ou até mesmo regiões geográficas diferentes.

Com o Azure Service Bus, você pode desacoplar componentes, permitindo que eles se comuniquem de maneira eficiente e segura (TEIXEIRA, 2020). Neste artigo, discutiremos como aplicar boas práticas no desenvolvimento de workers em .NET que consomem mensagens do Azure Service Bus, garantindo eficiência, manutenção e escalabilidade em suas aplicações.

Configurando o Ambiente

Para começar a trabalhar com o Azure Service Bus em .NET, você precisará configurar seu ambiente. Primeiramente, instale o pacote NuGet necessário para interagir com o Azure Service Bus. Execute o seguinte comando no seu console do NuGet:

Install-Package Azure.Messaging.ServiceBus

Em seguida, crie uma instância do Service Bus no portal do Azure e obtenha a string de conexão. A string de conexão contém informações essenciais, como o namespace e as credenciais necessárias para acessar o Azure Service Bus.

Estrutura Básica do Worker

Um worker é um serviço em segundo plano que processa mensagens. Abaixo está um exemplo de como você pode criar um worker simples que consome mensagens do Azure Service Bus:

using Azure.Messaging.ServiceBus;
using System;
using System.Threading.Tasks;

namespace WorkerService
{
    public class MessageProcessor
    {
        private readonly ServiceBusClient _client;
        private readonly ServiceBusProcessor _processor;

        public MessageProcessor(string connectionString, string queueName)
        {
            _client = new ServiceBusClient(connectionString);
            _processor = _client.CreateProcessor(queueName, new ServiceBusProcessorOptions());
        }

        public async Task StartProcessingAsync()
        {
            _processor.ProcessMessageAsync += MessageHandler;
            _processor.ProcessErrorAsync += ErrorHandler;

            await _processor.StartProcessingAsync();
            Console.WriteLine("Pressione qualquer tecla para parar...");
            Console.ReadKey();
            await _processor.StopProcessingAsync();
        }

        private async Task MessageHandler(ProcessMessageEventArgs args)
        {
            string body = args.Message.Body.ToString();
            Console.WriteLine($"Mensagem recebida: {body}");
            await args.CompleteMessageAsync(args.Message);
        }

        private Task ErrorHandler(ProcessErrorEventArgs args)
        {
            Console.WriteLine($"Erro: {args.Exception.Message}");
            return Task.CompletedTask;
        }
    }
}

Gerenciamento de Conexões

Uma das boas práticas ao trabalhar com o Azure Service Bus é gerenciar as conexões de forma eficiente. É importante evitar a criação de novas instâncias do ServiceBusClient a cada operação. Em vez disso, crie uma instância única e reutilize-a ao longo do ciclo de vida da aplicação. Isso não só melhora o desempenho, mas também reduz o consumo de recursos.

private static ServiceBusClient _client;

public static void Initialize(string connectionString)
{
    _client = new ServiceBusClient(connectionString);
}

Configurações e Reintentos

A configuração do ASB é importante para garantir que as mensagens não sejam perdidas em caso de falhas temporárias. O Azure Service Bus oferece suporte para reintentos automáticos. Você pode configurar as opções de processamento para lidar com erros de forma mais granular. Ao implementar uma lógica de reintento, você pode definir um número máximo de tentativas e uma estratégia de espera entre as tentativas, garantindo que seu worker continue a funcionar mesmo em situações adversas.

var options = new ServiceBusProcessorOptions
{
    MaxConcurrentCalls = 10,
    MaxAutoLockRenewalDuration = TimeSpan.FromMinutes(5),
    PrefetchCount = 100
};

Neste exemplo:

  • O cliente pode processar até 10 mensagens em paralelo (MaxConcurrentCalls);
  • Ele renova automaticamente o "lock" da mensagem por até 5 minutos;
  • Faz prefetch de 100 mensagens – ou seja, ele já carrega 100 mensagens da fila, antecipando o consumo, para otimizar o desempenho.

Escalabilidade e Desempenho

Ao projetar seu worker, considere a escalabilidade. Você pode aumentar a quantidade de instâncias do worker em execução para lidar com picos de demanda. O Azure Service Bus permite que você escale horizontalmente, aumentando o número de instâncias do worker em execução. Utilize o MaxConcurrentCalls para equilibrar a carga de mensagens entre as instâncias (REAGAN, 2017), o que ajuda a maximizar o desempenho. Além disso, monitore o desempenho da aplicação para identificar gargalos e otimizar a configuração conforme necessário.

Monitoramento e Logging

Application Insights

Implementar um bom sistema de monitoramento e logging é fundamental. Utilize ferramentas como Application Insights para rastrear o desempenho do seu worker e capturar exceções. O Azure oferece integração com o Application Insights, facilitando a coleta de dados sobre o desempenho da aplicação, como tempo de resposta e taxas de erro. Isso permitirá que você identifique rapidamente problemas e tome ações corretivas.

private static void ConfigureLogging()
{
    var loggerFactory = LoggerFactory.Create(builder =>
    {
        builder.AddApplicationInsights("", LogLevel.Information);
    });
    Logger = loggerFactory.CreateLogger("WorkerService");
}

Datadog

O Datadog é uma plataforma poderosa de monitoramento e observabilidade que permite coletar métricas, logs e traços distribuídos da sua aplicação. Ele se integra bem com serviços em nuvem, containers e arquiteturas distribuídas. Abaixo está um exemplo de como configurar o Datadog em uma aplicação .NET para enviar logs:

public static void ConfigureDatadogLogging()
{
    var loggerFactory = LoggerFactory.Create(builder =>
    {
        builder.AddDatadogLogs(options =>
        {
            options.ApiKey = "<YOUR_DATADOG_API_KEY>";
            options.Service = "WorkerService";
            options.Hostname = Environment.MachineName;
            options.Source = "csharp";
            options.Tags = new[] { "env:production", "team:backend" };
        });
    });

    Logger = loggerFactory.CreateLogger("WorkerService");
}

Você também pode enviar métricas personalizadas diretamente para o Datadog usando o cliente DogStatsD. Abaixo está um exemplo em que registramos a contagem de mensagens recebidas de uma fila do Azure Service Bus:

using StatsdClient;

public class MessageProcessor
{
    private static readonly Statsd statsd = new Statsd("127.0.0.1", 8125); // Endereço do agente Datadog

    public async Task ProcessMessageAsync(ServiceBusReceivedMessage message)
    {
        try
        {
            // Processa a mensagem
            // ...

            // Envia uma métrica personalizada para o Datadog
            statsd.Increment("azure_servicebus.messages_received", tags: new[] { "queue:orders", "env:production" });

            Logger.LogInformation("Mensagem processada com sucesso.");
        }
        catch (Exception ex)
        {
            statsd.Increment("azure_servicebus.processing_errors", tags: new[] { "queue:orders", "env:production" });
            Logger.LogError(ex, "Erro ao processar mensagem.");
        }
    }
}

Essas métricas podem ser visualizadas em dashboards do Datadog, e você pode configurar alertas para situações como aumento de falhas no processamento de mensagens ou variações no volume recebido.

Conclusão

A criação de workers em .NET para consumir mensagens do Azure Service Bus é uma tarefa que pode ser otimizada mediante a aplicação de boas práticas. Ao gerenciar conexões de forma eficaz, tratar erros adequadamente, monitorar o desempenho e garantir a segurança na comunicação, você pode construir sistemas robustos e escaláveis que atendem às necessidades de sua aplicação.

Referências

  • TEIXEIRA, Bruna Moreira. Arquiteturas baseadas em mensagens. Dissertação de Mestrado. Instituto Politecnico do Porto (Portugal). 2020. reference.Description
  • PIISPANEN, Juho. Microsoft Azure as an Integration Platform. 2022. reference.Description
  • REAGAN, Rob. Message Queues. In: Web Applications on Azure: Developing for Global Scale. Berkeley, CA: Apress, 2017. p. 343-380. reference.Description
Sobre o autor