Melhores práticas para uma melhoria do desempenho com as Mensagens do Service Bus

Este artigo descreve como usar o Barramento de Serviço do Azure para otimizar o desempenho ao trocar mensagens intermediadas. A primeira parte deste artigo descreve diferentes mecanismos para aumentar o desempenho. A segunda parte fornece orientação sobre como usar o Service Bus de uma forma que possa oferecer o melhor desempenho em um determinado cenário.

Ao longo deste artigo, o termo "cliente" refere-se a qualquer entidade que aceda ao Service Bus. Um cliente pode assumir o papel de emissor ou destinatário. O termo "remetente" é usado para um cliente de fila do Service Bus ou um cliente de tópico que envia mensagens para uma fila do Service Bus ou um tópico. O termo "recetor" refere-se a um cliente de fila do Service Bus ou cliente de assinatura que recebe mensagens de uma fila do Service Bus ou de uma assinatura.

Planeamento de recursos e considerações

Como acontece com qualquer recurso técnico, o planejamento prudente é fundamental para garantir que o Barramento de Serviço do Azure esteja fornecendo o desempenho esperado pelo seu aplicativo. A configuração ou topologia correta para seus namespaces do Service Bus depende de uma série de fatores que envolvem a arquitetura do aplicativo e como cada um dos recursos do Service Bus é usado.

Escalão de preço

O Service Bus oferece vários níveis de preços. É recomendável escolher a camada apropriada para os requisitos do seu aplicativo.

  • Camada padrão - Adequada para ambientes de desenvolvedor/teste ou cenários de baixa taxa de transferência em que os aplicativos não são sensíveis à limitação.

  • Nível Premium - Adequado para ambientes de produção com requisitos de throughput variados, onde latência e throughput previsíveis são necessários. Além disso, os namespaces premium do Service Bus podem ser dimensionados automaticamente e podem ser habilitados para acomodar picos na taxa de transferência.

Nota

Se a camada correta não for escolhida, há um risco de sobrecarregar o namespace do Service Bus, o que pode levar à limitação.

A limitação não leva à perda de dados. Os aplicativos que utilizam o SDK do Service Bus podem utilizar a política de repetição padrão para garantir que os dados sejam eventualmente aceitos pelo Service Bus.

Calculando a taxa de transferência para Premium

Os dados enviados para o Service Bus são serializados para binários e, em seguida, desserializados quando recebidos pelo recetor. Assim, enquanto os aplicativos pensam nas mensagens como unidades atômicas de trabalho, o Service Bus mede a taxa de transferência em termos de bytes (ou megabytes).

Ao calcular o requisito de taxa de transferência, considere os dados que estão sendo enviados para o Service Bus (entrada) e os dados recebidos do Service Bus (saída).

Como esperado, a taxa de transferência é maior para cargas úteis de mensagens menores que podem ser agrupadas em lote.

Testes de referências

Aqui está um exemplo do GitHub que você pode executar para ver a taxa de transferência esperada que você recebe para seu namespace do Service Bus. Em nossos testes de benchmark, observamos aproximadamente 4 MB/segundo por Unidade de Mensagens (MU) de entrada e saída.

O exemplo de benchmarking não usa recursos avançados, portanto, a taxa de transferência observada pelos aplicativos é diferente, com base em seus cenários.

Considerações sobre computação

O Service Bus opera vários processos em segundo plano que podem afetar a utilização da computação. Estes incluem, mas não estão limitados a, temporizadores, cronogramas e emissão de métricas. Além disso, o uso de determinados recursos do Service Bus requer a utilização de computação que pode diminuir a taxa de transferência esperada. Algumas dessas características são:

  1. Sessões.
  2. Oferecendo várias assinaturas em um único tópico.
  3. Executando muitos filtros em uma única assinatura.
  4. Mensagens agendadas.
  5. Mensagens adiadas.
  6. transações.
  7. Desduplicação & olhar para trás janela de tempo.
  8. Encaminhar para (encaminhamento de uma entidade para outra).

Se seu aplicativo usa qualquer um dos recursos acima e você não está recebendo a taxa de transferência esperada, você pode revisar as métricas de uso da CPU e considerar a expansão do namespace Premium do Service Bus. Você também pode utilizar o Azure Monitor para dimensionar automaticamente o namespace do Service Bus. Recomenda-se aumentar o número de unidades de mensagem (MUs) quando o uso da CPU excede 70% para garantir o desempenho ideal.

Compartilhamento entre namespaces

Embora a expansão da computação (unidades de mensagens) alocada para o namespace seja uma solução mais fácil, ela pode não fornecer um aumento linear na taxa de transferência. É por causa dos internos do Service Bus (armazenamento, rede, etc.), que podem estar limitando a taxa de transferência.

A solução mais limpa, nesse caso, é fragmentar suas entidades (filas e tópicos) em diferentes namespaces do Service Bus Premium. Você também pode considerar a fragmentação em namespaces diferentes em diferentes regiões do Azure.

Protocolos

O Service Bus permite que os clientes enviem e recebam mensagens por meio de um dos três protocolos:

  1. Advanced Message Queuing Protocol (AMQP)
  2. Protocolo de mensagens do Service Bus (SBMP)
  3. Protocolo HTTP (Hypertext Transfer Protocol)

O AMQP é o mais eficiente, pois mantém a conexão com o Service Bus. Ele também implementa loteamento e pré-busca. A menos que explicitamente mencionado, todo o conteúdo deste artigo pressupõe o uso de AMQP ou SBMP.

Importante

O protocolo SBMP só está disponível para o .NET Framework. AMQP é o padrão para .NET Standard.

Em 30 de setembro de 2026, desativaremos o suporte ao protocolo SBMP para o Barramento de Serviço do Azure, portanto, você não poderá mais usar esse protocolo após 30 de setembro de 2026. Migre para as bibliotecas mais recentes do SDK do Barramento de Serviço do Azure usando o protocolo AMQP, que oferecem atualizações de segurança críticas e recursos aprimorados, antes dessa data.

Para obter mais informações, consulte o anúncio de aposentadoria de suporte.

Escolhendo o SDK .NET do Service Bus apropriado

O Azure.Messaging.ServiceBus pacote é o SDK .NET do Azure Service Bus mais recente disponível em novembro de 2020. Há dois SDKs .NET mais antigos que continuarão a receber correções de bugs críticos até 30 de setembro de 2026, mas recomendamos fortemente que você use o SDK mais recente. Leia o guia de migração para obter detalhes sobre como migrar dos SDKs mais antigos.

Pacote NuGet Namespaces primários Plataformas Mínimas Protocolos
Azure.Messaging.ServiceBus (mais recente) Azure.Messaging.ServiceBus
Azure.Messaging.ServiceBus.Administration
.NET Core 2.0
.NET Framework 4.6.1
Mono 5,4
Plataforma Universal do Windows 10.0.16299
AMQP
HTTP
Microsoft.Azure.ServiceBus Microsoft.Azure.ServiceBus
Microsoft.Azure.ServiceBus.Management
.NET Core 2.0
.NET Framework 4.6.1
Mono 5,4
Plataforma Universal do Windows 10.0.16299
AMQP
HTTP

Para obter mais informações sobre o suporte mínimo à plataforma .NET Standard, consulte Suporte à implementação do .NET.

Em 30 de setembro de 2026, desativaremos as bibliotecas do SDK do Barramento de Serviço do Azure WindowsAzure.ServiceBus, Microsoft.Azure.ServiceBus e com.microsoft.azure.servicebus, que não estão em conformidade com as diretrizes do SDK do Azure. Também encerraremos o suporte ao protocolo SBMP, para que você não possa mais usar esse protocolo após 30 de setembro de 2026. Migre para as bibliotecas mais recentes do SDK do Azure, que oferecem atualizações de segurança críticas e recursos aprimorados, antes dessa data.

Embora as bibliotecas mais antigas ainda possam ser usadas após 30 de setembro de 2026, elas não receberão mais suporte e atualizações oficiais da Microsoft. Para obter mais informações, consulte o anúncio de aposentadoria de suporte.

Reutilização de fábricas e clientes

Os clientes do Service Bus que interagem com o serviço, como ServiceBusClient, ServiceBusSender, ServiceBusReceiver e ServiceBusProcessor, devem ser registrados para injeção de dependência como singletons (ou instanciados uma vez e compartilhados). ServiceBusClient pode ser registrado para injeção de dependência com ServiceBusClientBuilderExtensions.

Recomendamos que você não feche ou descarte esses clientes depois de enviar ou receber cada mensagem. Fechar ou descartar os objetos específicos da entidade (ServiceBusSender/Receiver/Processor) resulta na eliminação do link para o serviço do Service Bus. A eliminação do ServiceBusClient resulta na desativação da conexão com o serviço do Service Bus.

Esta orientação não se aplica ao ServiceBusSessionReceiver, pois seu tempo de vida é o mesmo da sessão em si. Para aplicativos que trabalham com o ServiceBusSessionReceiver, é recomendável usar uma instância singleton do ServiceBusClient para aceitar cada sessão, que abrange um novo ServiceBusSessionReceiver vinculado a essa sessão. Assim que o aplicativo terminar de processar essa sessão, ele deve descartar o arquivo ServiceBusSessionReceiver.

A seguinte observação aplica-se a todos os SDKs:

Nota

Estabelecer uma conexão é uma operação cara que você pode evitar reutilizando os mesmos objetos de fábrica ou cliente para várias operações. Você pode usar com segurança esses objetos cliente para operações assíncronas simultâneas e de vários threads.

Operações simultâneas

Operações como enviar, receber, excluir e assim por diante, levam algum tempo. Esse tempo inclui o tempo que o serviço do Service Bus leva para processar a operação e a latência da solicitação e da resposta. Para aumentar o número de operações por vez, as operações devem ser executadas simultaneamente.

O cliente agenda operações simultâneas executando operações assíncronas . A próxima solicitação é iniciada antes que a solicitação anterior seja concluída. O trecho de código a seguir é um exemplo de uma operação de envio assíncrona:

var messageOne = new ServiceBusMessage(body);
var messageTwo = new ServiceBusMessage(body);

var sendFirstMessageTask =
    sender.SendMessageAsync(messageOne).ContinueWith(_ =>
    {
        Console.WriteLine("Sent message #1");
    });
var sendSecondMessageTask =
    sender.SendMessageAsync(messageTwo).ContinueWith(_ =>
    {
        Console.WriteLine("Sent message #2");
    });

await Task.WhenAll(sendFirstMessageTask, sendSecondMessageTask);
Console.WriteLine("All messages sent");

O código a seguir é um exemplo de uma operação de recebimento assíncrona.

var client = new ServiceBusClient(connectionString);
var options = new ServiceBusProcessorOptions 
{

      AutoCompleteMessages = false,
      MaxConcurrentCalls = 20
};
await using ServiceBusProcessor processor = client.CreateProcessor(queueName,options);
processor.ProcessMessageAsync += MessageHandler;
processor.ProcessErrorAsync += ErrorHandler;

static Task ErrorHandler(ProcessErrorEventArgs args)
{
    Console.WriteLine(args.Exception);
    return Task.CompletedTask;
};

static async Task MessageHandler(ProcessMessageEventArgs args)
{
    Console.WriteLine("Handle message");
    await args.CompleteMessageAsync(args.Message);
}

await processor.StartProcessingAsync();

Modo de receção

Ao criar uma fila ou um cliente de assinatura, você pode especificar um modo de recebimento: Peek-lock ou Receive and Delete. O modo de recebimento padrão é PeekLock. Ao operar no modo padrão, o cliente envia uma solicitação para receber uma mensagem do Service Bus. Depois que o cliente recebe a mensagem, ele envia uma solicitação para concluir a mensagem.

Ao definir o modo de recebimento como ReceiveAndDelete, ambas as etapas são combinadas em uma única solicitação. Essas etapas reduzem o número geral de operações e podem melhorar a taxa de transferência geral de mensagens. Esse ganho de desempenho corre o risco de perder mensagens.

O Service Bus não oferece suporte a transações para operações de recebimento e exclusão. Além disso, a semântica peek-lock é necessária para todos os cenários em que o cliente deseja adiar ou escrever uma mensagem morta .

Pré-busca

A pré-busca permite que a fila ou o cliente de assinatura carregue mensagens extras do serviço quando receber mensagens. O cliente armazena essas mensagens em um cache local. O tamanho do cache é determinado pelas ServiceBusReceiver.PrefetchCount propriedades. Cada cliente que permite a pré-busca mantém seu próprio cache. Um cache não é compartilhado entre clientes. Se o cliente iniciar uma operação de recebimento e seu cache estiver vazio, o serviço transmitirá um lote de mensagens. Se o cliente iniciar uma operação de recebimento e o cache contiver uma mensagem, a mensagem será retirada do cache.

Quando uma mensagem é pré-buscada, o serviço bloqueia a mensagem pré-buscada. Com o bloqueio, a mensagem pré-buscada não pode ser recebida por um recetor diferente. Se o recetor não conseguir concluir a mensagem antes de o bloqueio expirar, a mensagem fica disponível para outros recetores. A cópia pré-buscada da mensagem permanece no cache. O recetor que consome a cópia em cache expirada recebe uma exceção quando tenta concluir essa mensagem. Por padrão, o bloqueio de mensagem expira após 60 segundos. Este valor pode ser estendido para 5 minutos. Para evitar o consumo de mensagens expiradas, defina o tamanho do cache menor do que o número de mensagens que um cliente pode consumir dentro do intervalo de tempo limite de bloqueio.

Quando você usa a expiração de bloqueio padrão de 60 segundos, um bom valor para PrefetchCount é 20 vezes as taxas máximas de processamento de todos os recetores de fábrica. Por exemplo, uma fábrica cria três recetores, e cada recetor pode processar até 10 mensagens por segundo. A contagem de pré-busca não deve exceder 20 X 3 X 10 = 600. Por padrão, PrefetchCount é definido como 0, o que significa que nenhuma mensagem extra é buscada no serviço.

A pré-busca de mensagens aumenta a taxa de transferência geral de uma fila ou assinatura porque reduz o número geral de operações de mensagens ou viagens de ida e volta. A busca da primeira mensagem, no entanto, leva mais tempo (devido ao aumento do tamanho da mensagem). O recebimento de mensagens pré-buscadas do cache é mais rápido porque essas mensagens já foram baixadas pelo cliente.

A propriedade time-to-live (TTL) de uma mensagem é verificada pelo servidor no momento em que o servidor envia a mensagem para o cliente. O cliente não verifica a propriedade TTL da mensagem quando a mensagem é recebida. Em vez disso, a mensagem pode ser recebida mesmo se o TTL da mensagem tiver passado enquanto a mensagem foi armazenada em cache pelo cliente.

A pré-busca não afeta o número de operações de mensagens faturáveis e está disponível apenas para o protocolo de cliente do Service Bus. O protocolo HTTP não suporta pré-busca. A pré-busca está disponível para operações de recebimento síncronas e assíncronas.

Para obter mais informações, consulte as seguintes PrefetchCount propriedades:

Você pode definir valores para essas propriedades em ServiceBusReceiverOptions ou ServiceBusProcessorOptions.

Pré-busca e ReceiveMessagesAsync

Embora os conceitos de pré-busca de várias mensagens juntas tenham semântica semelhante ao processamento de mensagens em um lote (ReceiveMessagesAsync), existem algumas pequenas diferenças que devem ser mantidas em mente ao usar essas abordagens juntas.

Prefetch é uma configuração (ou modo) no ServiceBusReceiver e ReceiveMessagesAsync é uma operação (que tem semântica solicitação-resposta).

Ao usar essas abordagens em conjunto, considere os seguintes casos:

  • A pré-busca deve ser maior ou igual ao número de mensagens que você espera receber do ReceiveMessagesAsync.
  • A pré-busca pode ser de até n/3 vezes o número de mensagens processadas por segundo, onde n é a duração de bloqueio padrão.

Existem alguns desafios em ter uma abordagem gananciosa, ou seja, manter a contagem de pré-busca alta, porque implica que a mensagem está bloqueada para um determinado recetor. Recomendamos que você experimente valores de pré-busca que estejam entre os limites mencionados anteriormente e identifique o que se encaixa.

Várias filas ou tópicos

Se uma única fila ou tópico não puder lidar com o número esperado de mensagens, use várias entidades de mensagens. Ao usar várias entidades, crie um cliente dedicado para cada entidade, em vez de usar o mesmo cliente para todas as entidades.

Mais filas ou tópicos significam que você tem mais entidades para gerenciar no momento da implantação. Do ponto de vista da escalabilidade, realmente não há muita diferença que você notaria, pois o Service Bus já distribui a carga por vários logs internamente, portanto, se você usar seis filas ou tópicos ou duas filas ou tópicos não fará uma diferença material.

A camada de serviço que você usa afeta a previsibilidade do desempenho. Se você escolher a camada Padrão , a taxa de transferência e a latência serão o melhor esforço em relação a uma infraestrutura multilocatária compartilhada. Outros locatários no mesmo cluster podem afetar sua taxa de transferência. Se você escolher Premium, obterá recursos que oferecem desempenho previsível e suas várias filas ou tópicos serão processados fora desse pool de recursos. Para obter mais informações, consulte Níveis de preços.

Namespaces particionados

Quando você usa namespaces de camada premium particionados, várias partições com unidades de mensagens (MU) mais baixas oferecem um melhor desempenho em relação a uma única partição com MUs mais altas.

Cenários

As seções a seguir descrevem cenários típicos de mensagens e descrevem as configurações preferenciais do Service Bus. As taxas de transferência são classificadas como pequenas (menos de 1 mensagem/segundo), moderadas (1 mensagem/segundo ou maior, mas menos de 100 mensagens/segundo) e altas (100 mensagens/segundo ou superior). O número de clientes é classificado como pequeno (5 ou menos), moderado (mais de 5 mas menor ou igual a 20) e grande (mais de 20).

Fila de alta taxa de transferência

Meta: maximizar a taxa de transferência de uma única fila. O número de remetentes e recetores é pequeno.

  • Para aumentar a taxa geral de envio para a fila, use várias fábricas de mensagens para criar remetentes. Para cada remetente, use operações assíncronas ou vários threads.
  • Para aumentar a taxa geral de recebimento da fila, use várias fábricas de mensagens para criar recetores.
  • Defina a contagem de pré-busca para 20 vezes as taxas máximas de processamento de todos os recetores de uma fábrica. Essa contagem reduz o número de transmissões de protocolo de cliente do Service Bus.

Várias filas de alta taxa de transferência

Meta: maximizar a taxa de transferência geral de várias filas. A taxa de transferência de uma fila individual é moderada ou alta.

Para obter a taxa de transferência máxima em várias filas, use as configurações descritas para maximizar a taxa de transferência de uma única fila. Além disso, use fábricas diferentes para criar clientes que enviam ou recebem de filas diferentes.

Fila de baixa latência

Meta: Minimizar a latência de uma fila ou tópico. O número de remetentes e recetores é pequeno. A taxa de transferência da fila é pequena ou moderada.

  • Se estiver usando um único cliente, defina a contagem de pré-busca para 20 vezes a taxa de processamento do recetor. Se várias mensagens chegarem à fila ao mesmo tempo, o protocolo de cliente do Service Bus transmitirá todas elas ao mesmo tempo. Quando o cliente recebe a próxima mensagem, essa mensagem já está no cache local. O cache deve ser pequeno.
  • Se estiver usando vários clientes, defina a contagem de pré-busca como 0. Ao definir a contagem, o segundo cliente pode receber a segunda mensagem enquanto o primeiro cliente ainda está processando a primeira mensagem.

Fila com um grande número de remetentes

Meta: maximizar a taxa de transferência de uma fila ou tópico com um grande número de remetentes. Cada remetente envia mensagens com uma taxa moderada. O número de recetores é pequeno.

O Service Bus permite até 1.000 conexões simultâneas com uma entidade de mensagens. Esse limite é imposto no nível do namespace e filas, tópicos ou assinaturas são limitados pelo limite de conexões simultâneas por namespace. Para filas, esse número é compartilhado entre remetentes e destinatários. Se todas as 1.000 conexões forem necessárias para remetentes, substitua a fila por um tópico e uma única assinatura. Um tópico aceita até 1.000 conexões simultâneas de remetentes. A assinatura aceita 1.000 conexões simultâneas extras de recetores. Se forem necessários mais de 1.000 remetentes simultâneos, os remetentes deverão enviar mensagens para o protocolo do Service Bus via HTTP.

Para maximizar a taxa de transferência, siga estas etapas:

  • Se cada remetente estiver em um processo diferente, use apenas uma única fábrica por processo.
  • Defina a contagem de pré-busca para 20 vezes as taxas máximas de processamento de todos os recetores de uma fábrica. Essa contagem reduz o número de transmissões de protocolo de cliente do Service Bus.

Fila com um grande número de recetores

Objetivo: Maximizar a taxa de recebimento de uma fila ou assinatura com um grande número de recetores. Cada recetor recebe mensagens a uma taxa moderada. O número de remetentes é pequeno.

O Service Bus permite até 1.000 conexões simultâneas com uma entidade. Se uma fila exigir mais de 1.000 recetores, substitua-a por um tópico e várias assinaturas. Cada assinatura pode suportar até 1.000 conexões simultâneas. Como alternativa, os recetores podem acessar a fila através do protocolo HTTP.

Para maximizar a taxa de transferência, siga estas diretrizes:

  • Se cada recetor estiver em um processo diferente, use apenas uma única fábrica por processo.
  • Defina a contagem de pré-busca para um valor pequeno (por exemplo, PrefetchCount = 10). Essa contagem evita que os recetores fiquem ociosos enquanto outros recetores têm um grande número de mensagens armazenadas em cache.

Tópico com algumas subscrições

Meta: maximizar a taxa de transferência de um tópico com algumas assinaturas. Uma mensagem é recebida por muitas assinaturas, o que significa que a taxa de recebimento combinada em todas as assinaturas é maior do que a taxa de envio. O número de remetentes é pequeno. O número de recetores por assinatura é pequeno.

Para maximizar a taxa de transferência, siga estas diretrizes:

  • Para aumentar a taxa geral de envio para o tópico, use várias fábricas de mensagens para criar remetentes. Para cada remetente, use operações assíncronas ou vários threads.
  • Para aumentar a taxa de recebimento geral de uma assinatura, use várias fábricas de mensagens para criar recetores. Para cada recetor, use operações assíncronas ou vários threads.
  • Defina a contagem de pré-busca para 20 vezes as taxas máximas de processamento de todos os recetores de uma fábrica. Essa contagem reduz o número de transmissões de protocolo de cliente do Service Bus.

Tópico com um grande número de subscrições

Meta: maximizar a taxa de transferência de um tópico com um grande número de assinaturas. Uma mensagem é recebida por muitas assinaturas, o que significa que a taxa de recebimento combinada em todas as assinaturas é maior do que a taxa de envio. O número de remetentes é pequeno. O número de recetores por assinatura é pequeno.

Os tópicos com um grande número de assinaturas normalmente expõem uma baixa taxa de transferência geral se todas as mensagens forem roteadas para todas as assinaturas. É porque cada mensagem é recebida muitas vezes, e todas as mensagens em um tópico e todas as suas assinaturas são armazenadas na mesma loja. A suposição aqui é que o número de remetentes e o número de recetores por assinatura é pequeno. O Service Bus suporta até 2.000 assinaturas por tópico.

Para maximizar a taxa de transferência, tente as seguintes etapas:

  • Defina a contagem de pré-busca para 20 vezes a taxa esperada na qual as mensagens são recebidas. Essa contagem reduz o número de transmissões de protocolo de cliente do Service Bus.