Exportar (0) Imprimir
Expandir Tudo

Práticas recomendadas para melhorias de desempenho usando o sistema de mensagens agenciado do Service Bus

Atualizado: junho de 2014

Este tópico descreve como usar o Microsoft Azure Service Bus para otimizar o desempenho na troca de mensagens agenciadas. A primeira metade deste tópico descreve os diferentes mecanismos oferecidos para ajudar a melhorar o desempenho. A segunda metade apresenta uma orientação sobre como usar o Service Bus de forma a oferecer o melhor desempenho para determinada situação.

Ao longo deste tópico, o termo “cliente” se refere a qualquer entidade que acessa o Service Bus. Um cliente pode representar a função de um remetente ou receptor. O termo “remetente” é usado para um cliente de fila ou tópico do Service Bus que envia mensagens a uma fila ou tópico do Service Bus. O termo “receptor” se refere a um cliente de fila ou assinatura do Service Bus que recebe mensagens de uma fila ou assinatura do Service Bus.

Esta seção apresenta diferentes conceitos empregados pelo Service Bus para ajudar a melhorar o desempenho.

O Service Bus permite que os clientes enviem e recebam mensagens por meio de dois protocolos: o protocolo de cliente Service Bus e HTTP. O protocolo de cliente Service Bus é mais eficiente porque mantém a conexão com o serviço Service Bus enquanto existir a fábrica de mensagens. Ele também implementa o envio em lote e a pré-busca. O protocolo de cliente Service Bus está disponível para aplicativos .NET que usam a API gerenciada por .NET.

A menos que mencionado explicitamente, todo o conteúdo deste tópico supõe o uso do protocolo de cliente Service Bus.

Os objetos cliente do Service Bus, como QueueClient ou MessageSender, são criados por meio de um objeto MessagingFactory, que também oferece gerenciamento interno de conexões. Não se deve fechar fábricas ou filas do sistema de mensagens, tópicos e clientes de assinatura depois de enviar uma mensagem, para então recriá-los ao enviar a próxima mensagem. Fechar uma fábrica de mensagens exclui a conexão com o serviço Service Bus e se estabelece uma nova conexão ao se recriar a fábrica. Estabelecer uma conexão é uma operação cara que pode ser evitada reutilizando-se os mesmos objetos de fábrica e cliente para diversas operações.

Realizar uma operação (enviar, receber, excluir etc.) toma algum tempo. Esse tempo abrange o processamento da operação pelo serviço Service Bus, além da latência da solicitação e da resposta. Para aumentar o número de operações por tempo, é preciso que as operações sejam executadas simultaneamente. Isso pode ser feito de várias maneiras:

  • Operações assíncronas: o cliente distribui operações realizando operações assíncronas. A próxima solicitação é iniciada antes da conclusão da solicitação anterior. Eis um exemplo de uma operação assíncrona de envio:

    BrokeredMessage m1 = new BrokeredMessage(body);
    BrokeredMessage m2 = new BrokeredMessage(body);
    queueClient.BeginSend(m1, processEndSend, queueClient); // Send message 1.
    queueClient.BeginSend(m2, processEndSend, queueClient); // Send message 2.
    
    void processEndSend(IAsyncResult result)
    {
        QueueClient qc = result.AsyncState as QueueClient;
        qc.EndSend(result);
        Console.WriteLine("Message sent");
    }
    
    Eis um exemplo de uma operação assíncrona de recebimento:

    queueClient.BeginReceive(processEndReceive, queueClient); // Receive message 1.
    queueClient.BeginReceive(processEndReceive, queueClient); // Receive message 2.
    
    void processEndReceive(IAsyncResult result) 
    {
        QueueClient qc = result.AsyncState as QueueClient;
        BrokeredMessage m = qc.EndReceive(result);
        m.BeginComplete(processEndComplete, m);
        Console.WriteLine("Received message " + m.Label);
    }
    
    void processEndComplete(IAsyncResult result)
    {
        BrokeredMessage m = result.AsyncState as BrokeredMessage;
        m.EndComplete(result);
        Console.WriteLine("Completed message " + m.Label);
    }
    
  • Diversas fábricas: todos os clientes (remetentes além dos receptores) criados pela mesma fábrica compartilham uma conexão TCP. O rendimento máximo de mensagens é limitado pelo número de operações que podem ser realizadas por meio dessa conexão TCP. O rendimento que pode ser obtido por uma única fábrica varia muito, dependendo do tempo de ida e volta do TCP e do tamanho da mensagem. Para obter taxas de resultado superiores, é preciso usar diversas fábricas de mensagens.

Ao criar um cliente de fila ou assinatura, é possível especificar um modo de recebimento: Espiada-bloqueio ou Receber e excluir. O modo padrão de recebimento é PeekLock. Ao operar nesse modo, 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.

Quando o modo de recebimento está configurado como ReceiveAndDelete, ambas as etapa são combinadas em uma única solicitação. Isso reduz o número geral de operações e pode melhorar o rendimento geral das mensagens. Este ganho de desempenho vem com o risco de perda de mensagens.

O Service Bus não é compatível com transações para operações receber-e-excluir. Além disso, a semântica espiada-bloqueio é necessária para qualquer situação em que o cliente deseje adiar ou definir uma mensagem como mensagem morta.

O envio em lote no lado do cliente permite que um cliente de fila ou tópico atrase o envio de uma mensagem por um determinado período. Se o cliente enviar mensagens adicionais durante esse período, ele transmitirá as mensagens em um só lote. O envio em lote no lado do cliente também faz com que o cliente de fila/assinatura envie em lote diversas solicitações de Concluir em uma única solicitação. O envio em lote está disponível somente para operações de Enviar e Concluir assíncronas. As operações síncronas são imediatamente enviadas ao serviço Service Bus. O envio em lote não ocorre para operações de inspecionamento ou recebimento, nem para os clientes.

Se o lote exceder o tamanho máximo de mensagem, a última mensagem é removida do lote e o cliente o envia imediatamente. A última mensagem se torna a primeira mensagem do próximo lote. Por padrão, um cliente usa um intervalo de envio em lote de 20 ms. É possível alterar o intervalo do envio em lote configurando a propriedade BatchFlushInterval antes de criar a fábrica do sistema de mensagens. Essa configuração afeta todos os clientes criados por essa fábrica. Para desativar o envio em lotes, defina a propriedade BatchFlushInterval como TimeSpan.Zero. Por exemplo:

MessagingFactorySettings mfs = new MessagingFactorySettings();
mfs.TokenProvider = tokenProvider;
mfs.NetMessagingTransportSettings.BatchFlushInterval = TimeSpan.FromSeconds(0.05);
MessagingFactory messagingFactory = MessagingFactory.Create(namespaceUri, mfs);

O envio em lote não afeta o número de operações de sistema de mensagens faturáveis e está disponível somente para o protocolo de cliente Service Bus. O protocolo HTTP não é compatível com o envio em lote.

Para aumentar o rendimento de uma fila/tópico/assinatura, o serviço Service Bus agrupa várias mensagens ao gravar em seu repositório interno. Se habilitada em uma fila ou tópico, a gravação de mensagens no repositório será feita em lotes. Se habilitada em uma fila ou assinatura, a exclusão de mensagens do repositório será feita em lotes. Se o acesso ao repositório em lotes estiver habilitado para uma entidade, o Service Bus atrasa uma operação de gravação no repositório em relação àquela entidade por até 20 ms. As operações adicionais do repositório que ocorrem durante esse intervalo são adicionadas ao lote. O acesso ao repositório em lote só afeta as operações Enviar e Concluir; as operações de recebimento não são afetadas. O acesso ao repositório em lote é uma propriedade em uma entidade. O envio em lote ocorre por todas as entidades que habilitam o acesso ao repositório em lote.

Ao criar uma nova fila, tópico ou assinatura, o acesso ao repositório em lote é habilitado por padrão. Para desabilitar o acesso ao repositório em lote, defina a propriedade EnableBatchedOperations como false antes de criar a entidade. Por exemplo:

QueueDescription qd = new QueueDescription();
qd.EnableBatchedOperations = false;
Queue q = namespaceManager.CreateQueue(qd);

O acesso ao repositório em lote não afeta o número de operações de sistema de mensagens faturáveis, e é uma propriedade de uma fila, tópico ou assinatura. É independente do modo de recebimento e do protocolo usado entre um cliente e o serviço Service Bus.

A pré-busca ativa um cliente de fila ou assinatura para carregar mensagens adicionais de um serviço quando ele realiza uma operação de recebimento. O cliente armazena essas mensagens em um cache local. O tamanho do cache é determinado pelas propriedades PrefetchCount e PrefetchCount. Cada cliente que permite a pré-busca mantém seu próprio cache. Os caches não são compartilhados entre clientes. Se o cliente inicia uma operação de recebimento e seu cache está vazio, o serviço transmite um lote de mensagens. O tamanho do lote é igual ao tamanho do cache ou 256 KB, o que for menor. Se o cliente iniciar uma operação de recebimento e o cache contiver uma mensagem, esta é retirada do cache.

Quando ocorre uma pré-busca de uma mensagem, o serviço bloqueia a mensagem de pré-busca. Assim, a mensagem de pré-busca não pode ser recebida por um receptor diferente. Se o receptor não puder concluir a mensagem antes de o bloqueio expirar, esta fica disponível para outros receptores. A cópia de pré-busca da mensagem permanece no cache. O receptor que consome a cópia expirada armazenada no cache receberá uma exceção quando tentar concluir essa mensagem. Por padrão, o bloqueio da mensagem expira após 60 segundos. Esse valor pode ser estendido por até 5 minutos. Para evitar o consumo de mensagens expiradas, o tamanho do cache deve sempre ser menor que o número de mensagens que podem ser consumidas por um cliente dentro de um intervalo de tempo limite.

Ao usar a expiração do bloqueio padrão de 60 segundos, um bom valor para SubscriptionClient.PrefetchCount é 20 vezes as taxas de processamento máximas de todos os receptores da fábrica. Por exemplo, uma fábrica cria 3 receptores. Cada receptor pode processar até 10 mensagens por segundo. A contagem de pré-busca não deve exceder 20*3*10 = 600. Por padrão, QueueClient.PrefetchCount é definido como 0, o que significa que nenhuma mensagem adicional é buscada a partir do serviço.

As mensagens de pré-busca aumentam o rendimento geral para uma fila ou assinatura porque reduz o número geral de operações de mensagens, ou de ida e volta. A busca da primeira mensagem, no entanto, demorará mais (devido ao maior tamanho da mensagem). O recebimento das mensagens de pré-busca será mais rápido porque elas já terão sido baixadas pelo clientes.

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

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

Entidades expressas habilitam uma alta taxa de transferência e cenários de latência reduzidos. Com entidades expressas, se uma mensagem for enviada para uma fila ou tópico, ela não será imediatamente armazenada no repositório de mensagens. Em vez disso, a mensagem é armazenada em cache na memória. Se uma mensagem permanecer na fila por mais do que alguns segundos, ela será automaticamente gravada no armazenamento estável. Isso protege a mensagem contra a perda de dados durante uma interrupção. Registrar a mensagem em um cache de memória aumenta o rendimento e reduz a latência, pois não há acesso ao armazenamento estável no momento que a mensagem é enviada. Mensagens que são consumidas dentro de alguns segundos não são registradas no repositório de mensagens. O exemplo a seguir cria um tópico expresso:

TopicDescription td = new TopicDescription(TopicName);
td.EnableExpress = true;
namespaceManager.CreateTopic(td);

Se uma mensagem contendo informações importantes que não devem ser perdidas for enviada para uma entidade expressa, o remetente pode forçar Service Bus a persistir imediatamente com a mensagem para o armazenamento estável, definindo a propriedade ForcePersistence para true. Para obter mais informações, consulte Novidades da versão Azure SDK 2,3 (abril de 2014).

Internamente, o Service Bus usa o mesmo nó e repositório de mensagens para processar e armazenar todas as mensagens para uma entidade de mensagem (fila ou tópico). Uma fila ou tópico particionado, por outro lado, é distribuído através de vários nós e repositórios de mensagens. Filas ou tópicos particionados não somente rendem um resultado maior que as filas e tópicos comuns, eles também exibem disponibilidade superior. Para criar um entidade particionada, defina a propriedade EnablePartitioning como true, conforme mostrado no exemplo a seguir. Para obter mais informações sobre entidades particionadas, consulte Entidades de mensagens de particionamento.

// Create partitioned queue.
QueueDescription qd = new QueueDescription(QueueName);
qd.EnablePartitioning = true;
namespaceManager.CreateQueue(qd);

Se não for possível usar uma fila ou tópico particionado, ou a carga esperada não puder ser manipulada por uma única fila ou tópico particionado, é necessário usar várias entidades de mensagem. Ao usar estas várias entidades, crie um cliente dedicado para cada uma delas em vez de usar o mesmo cliente para todas as entidades.

As seções a seguir descrevem as situações típicas de mensagens e delineiam as configurações preferenciais do Service Bus. As taxas de rendimento são classificadas como baixa (<1 msg/s), moderada (≥1msg/s, <100 msg/s) e alta (≥100 msg/s). Os números de clientes são classificados como pequeno (>5), moderado (>5, ≤20) e grande (>20).

Meta: maximizar o rendimento de uma única fila. O número de remetentes e receptores é pequeno.

  • Use uma fila particionada para melhor desempenho e disponibilidade.

  • Para aumentar a taxa geral de envio para a fila, use diversas fábricas de mensagens para criar remetentes. Para cada remetente, use operações assíncronas ou mais de um thread.

  • Para aumentar a taxa geral de recebimento da fila, use diversas fábricas de mensagens para criar receptores.

  • Use operações assíncronas para aproveitar o envio em lote no lado do cliente.

  • Defina o intervalo do envio em lote como 50 ms para reduzir o número de transmissões de protocolo de cliente do Service Bus. Se for usar diversos remetentes, aumente o intervalo do envio em lote para 100 ms.

  • Deixe o acesso ao repositório em lote habilitado. Isso aumenta a taxa geral com que as mensagens podem ser gravadas na fila.

  • Defina a contagem de pré-busca como 20 vezes as taxas de processamento máximas de todos os receptores de uma fábrica. Isso reduz o número de transmissões de protocolo de cliente do Service Bus.

Meta: maximizar o rendimento máximo de diversas filas. O rendimento de uma fila individual é moderado ou alto.

Para obter o máximo de rendimento por diversas filas, use as configurações descritas para maximizar o rendimento de uma única fila. Além disso, use diferentes fábricas para criar clientes que enviam ou recebem de diferentes filas.

Meta: minimizar a latência de ponta a ponta de uma fila ou tópico. O número de remetentes e receptores é pequeno. O rendimento da fila é pequeno ou moderado.

  • Use uma fila particionada para melhor disponibilidade.

  • Desative o envio em lote no lado do cliente. O cliente envia imediatamente uma mensagem.

  • Desabilite o acesso ao repositório em lote. O serviço grava imediatamente a mensagem no repositório.

  • Se for usar um cliente único, defina a contagem de pré-busca para 20 vezes a taxa de processamento do receptor. Se diversas mensagens chegarem à fila na mesma hora, o protocolo de cliente do Service Bus as transmite todas ao mesmo tempo. Quando o cliente receber a próxima mensagem, ela já estará no cache local. O cache deve ser pequeno.

  • Se for usar diversos clientes, defina a contagem de pré-busca como 0. Assim, o segundo cliente pode receber a segunda mensagem enquanto o primeiro cliente ainda processa a primeira mensagem.

Meta: maximizar o rendimento 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 receptores é pequeno.

O Service Busativa até 100 conexões simultâneas para uma entidade. Para as filas, esse número é compartilhado entre remetentes e receptores. Se todas as 100 conexões forem necessárias para os remetentes, deve-se substituir a fila por um tópico e uma única assinatura. Um tópico aceita até 100 conexões simultâneas dos remetentes, considerando que a assinatura aceita um adicional de 100 conexões simultâneas dos receptores. Se forem necessários mais de 100 remetentes simultâneos, estes devem enviar mensagens ao protocolo Service Bus por meio de HTTP.

Para maximizar o rendimento, faça o seguinte:

  • Use uma fila particionada para melhor desempenho e disponibilidade.

  • Se cada remetente residir em um processo diferente, use somente uma fábrica por processo.

  • Use operações assíncronas para aproveitar o envio em lote no lado do cliente.

  • Use o intervalo do envio em lote de 20 ms para reduzir o número de transmissões de protocolo de cliente do Service Bus.

  • Deixe o acesso ao repositório em lote habilitado. Isso aumenta a taxa geral com que as mensagens podem ser gravadas na fila ou tópico.

  • Defina a contagem de pré-busca como 20 vezes as taxas de processamento máximas de todos os receptores de uma fábrica. Isso reduz o número de transmissões de protocolo de cliente do Service Bus.

Meta: maximizar a taxa de recebimento de uma fila ou assinatura com um grande número de receptores. Cada receptor recebe mensagens a uma taxa moderada. O número de remetentes é pequeno.

O Service Busativa até 100 conexões simultâneas para uma entidade. Se forem necessários mais de 100 receptores para uma fila, deve-se substituir a fila por um tópico e diversas assinaturas. Cada assinatura pode suportar até 100 conexões simultâneas. Como alternativa, os receptores podem acessar a fila por meio do protocolo HTTP.

Para maximizar o rendimento, faça o seguinte:

  • Use uma fila particionada para melhor desempenho e disponibilidade.

  • Se cada receptor residir em um processo diferente, use somente uma fábrica por processo.

  • Os receptores podem usar operações síncronas ou assíncronas. Dada a taxa de recebimento moderada de um receptor individual, o envio em lote no lado do cliente de uma solicitação de Concluir não afeta o rendimento do receptor.

  • Deixe o acesso ao repositório em lote habilitado. Isso reduz a carga geral de uma entidade. Isso também reduz a taxa geral com que as mensagens podem ser gravadas na fila ou tópico.

  • Defina a contagem de pré-busca como um valor pequeno (por exemplo, PrefetchCount = 10). Isso evita que os receptores fiquem inativos enquanto outros têm grandes quantidades de mensagens em cache.

Meta: maximizar o rendimento de um tópico com um pequeno número de assinaturas. Uma mensagem é recebida por muitas assinaturas, o que significa que a taxa combinada de recepção de todas as assinaturas é maior que a taxa de envio. O número de remetentes é pequeno. O número de receptores por assinatura é pequeno.

Para maximizar o rendimento, faça o seguinte:

  • Use um tópico particionado para melhor desempenho e disponibilidade.

  • Para aumentar a taxa geral de envio para o tópico, use diversas fábricas de mensagens para criar remetentes. Para cada remetente, use operações assíncronas ou mais de um thread.

  • Para aumentar a taxa geral de recebimento de uma assinatura, use diversas fábricas de mensagens para criar receptores. Para cada receptor, use operações assíncronas ou mais de um thread.

  • Use operações assíncronas para aproveitar o envio em lote no lado do cliente.

  • Use o intervalo do envio em lote de 20 ms para reduzir o número de transmissões de protocolo de cliente do Service Bus.

  • Deixe o acesso ao repositório em lote habilitado. Isso aumenta a taxa geral com que as mensagens podem ser gravadas no tópico.

  • Defina a contagem de pré-busca como 20 vezes as taxas de processamento máximas de todos os receptores de uma fábrica. Isso reduz o número de transmissões de protocolo de cliente do Service Bus.

Meta: maximizar o rendimento de um tópico com um grande número de assinaturas. Uma mensagem é recebida por muitas assinaturas, o que significa que a taxa combinada de recepção por todas as assinaturas é muito maior que a taxa de envio. O número de remetentes é pequeno. O número de receptores por assinatura é pequeno.

Os tópicos com um grande número de assinaturas normalmente expõem um rendimento geral baixo se todas as mensagens forem roteadas para todas as assinaturas. Isso é causado pelo fato de que cada mensagem é recebida várias vezes e todas as mensagens contidas em um tópico e todas as suas assinaturas são armazenadas no mesmo repositório. Supõe-se que o número de remetentes e de receptores por assinatura seja pequeno. O Service Bus é compatível com até 2.000 assinatura por tópico.

Para maximizar o rendimento, faça o seguinte:

  • Use um tópico particionado para melhor desempenho e disponibilidade.

  • Use operações assíncronas para aproveitar o envio em lote no lado do cliente.

  • Use o intervalo do envio em lote de 20 ms para reduzir o número de transmissões de protocolo de cliente do Service Bus.

  • Deixe o acesso ao repositório em lote habilitado. Isso aumenta a taxa geral com que as mensagens podem ser gravadas no tópico.

  • Defina a contagem de pré-busca para 20 vezes a taxa de recebimento esperada em segundos. Isso reduz o número de transmissões de protocolo de cliente do Service Bus.

Mostrar:
© 2014 Microsoft