Partilhar via


Transferências de mensagens, bloqueios e acordo

A capacidade central de um agente de mensagens, como o Service Bus, é aceitar mensagens em uma fila ou tópico e mantê-las disponíveis para recuperação posterior. O termo enviar é usado para descrever o processo de transferência de uma mensagem para o agente de mensagens, enquanto receber refere-se ao processo de recuperar uma mensagem do corretor.

Quando um cliente envia uma mensagem, ele geralmente quer saber se a mensagem é devidamente transferida e aceita pelo corretor ou se ocorreu algum tipo de erro. Este reconhecimento positivo ou negativo estabelece o entendimento do cliente e do corretor sobre o estado de transferência da mensagem. Portanto, chama-se um assentamento.

Da mesma forma, quando o broker transfere uma mensagem para um cliente, o broker e o cliente querem estabelecer um entendimento sobre se a mensagem é processada com êxito e, portanto, pode ser removida, ou se a entrega ou o processamento da mensagem falhou e, portanto, a mensagem pode ter que ser entregue novamente.

Liquidação de operações de envio

Em qualquer um dos clientes suportados da API do Service Bus, as operações de envio para o Service Bus são sempre confirmadas explicitamente, o que significa que a operação da API aguarda a chegada de um resultado de aceitação do Service Bus antes de concluir a operação de envio.

Se a mensagem for rejeitada pelo Service Bus, a rejeição conterá um indicador de erro e texto com um ID de rastreamento. A rejeição também inclui informações sobre se a operação pode ser repetida com alguma expectativa de sucesso. No cliente, essa informação é transformada em exceção e propagada para o utilizador que chamou a função de envio. Se a mensagem for aceite, a operação é concluída silenciosamente.

O AMQP (Advanced Messaging Queuing Protocol) é o único protocolo suportado para clientes .NET Standard, Java, JavaScript, Python e Go. Para clientes .NET Framework, pode usar o Service Bus Messaging Protocol (SBMP) ou AMQP. Quando se utiliza o protocolo AMQP, as transferências e liquidações de mensagens são processadas em pipeline e de forma assíncrona. Recomendamos que você use as variantes da API do modelo de programação assíncrona.

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 descontinuação do suporte.

Um remetente pode colocar várias mensagens no fio em rápida sucessão sem ter que esperar que cada mensagem seja reconhecida, como seria o caso com o protocolo SBMP ou com HTTP 1.1. Essas operações de envio assíncronas são concluídas à medida que as respetivas mensagens são aceitas e armazenadas, em entidades particionadas ou quando a operação de envio para entidades diferentes se sobrepõe. As finalizações também podem ocorrer fora da ordem de envio original.

A estratégia para lidar com o resultado das operações de envio pode ter um impacto imediato e significativo no desempenho do seu aplicativo. Os exemplos nesta seção são escritos em C# e aplicam-se a futures do Java, monos do Java, promessas do JavaScript e conceitos equivalentes noutras linguagens.

Se a aplicação produz explosões de mensagens, ilustradas aqui com um loop simples, e aguardasse a conclusão de cada operação de envio antes de enviar a próxima mensagem, quer fosse de forma síncrona ou assíncrona, o envio de 10 mensagens só se completaria após 10 sequências completas de ida e volta para processamento.

Com uma latência assumida de ida e volta do Protocolo de Controlo de Transmissão (TCP) de 70 milissegundos desde instalações locais até ao Service Bus e alocando apenas 10 ms para o Service Bus aceitar e armazenar cada mensagem, o loop seguinte demora pelo menos 8 segundos, sem contar o tempo de transferência de carga útil ou os possíveis efeitos de congestionamento de rota.

for (int i = 0; i < 10; i++)
{
    // creating the message omitted for brevity
    await sender.SendMessageAsync(message);
}

Se o aplicativo iniciar as 10 operações de envio assíncronas em sucessão imediata e aguardar sua respetiva conclusão separadamente, o tempo de ida e volta para essas 10 operações de envio se sobrepõe. As 10 mensagens são transferidas em sucessão imediata, potencialmente até mesmo compartilhando quadros TCP, e a duração total da transferência depende em grande parte do tempo relacionado à rede necessário para transferir as mensagens para o intermediário.

Com as mesmas suposições do loop anterior, o tempo total de execução sobreposto para o loop a seguir pode ficar bem abaixo de um segundo:

var tasks = new List<Task>();
for (int i = 0; i < 10; i++)
{
    tasks.Add(sender.SendMessageAsync(message));
}
await Task.WhenAll(tasks);

É importante observar que todos os modelos de programação assíncrona usam alguma forma de fila de trabalho oculta baseada em memória que mantém operações pendentes. Quando a API de envio retorna, a tarefa de envio é enfileirada nessa fila de trabalho, mas o gesto de protocolo só começa quando é a vez da tarefa ser executada. Para códigos que tendem a enviar rajadas de mensagens e onde a confiabilidade é uma preocupação, deve-se tomar cuidado para que não muitas mensagens sejam colocadas "em voo" ao mesmo tempo, porque todas as mensagens enviadas ocupam memória até serem colocadas no fio.

Os semáforos, conforme mostrado no trecho de código a seguir em C#, são objetos de sincronização que permitem essa limitação a nível da aplicação, sempre que necessário. Este uso de um semáforo permite que no máximo 10 mensagens estejam em voo ao mesmo tempo. Um dos 10 bloqueios de semáforo disponíveis é tirado antes do envio e é liberado quando o envio é concluído. A 11ª passagem pelo loop aguarda até que pelo menos uma das operações de envio anteriores seja concluída e, em seguida, disponibiliza seu bloqueio:

var semaphore = new SemaphoreSlim(10);

var tasks = new List<Task>();
for (int i = 0; i < 10; i++)
{
    await semaphore.WaitAsync();

    tasks.Add(sender.SendMessageAsync(message).ContinueWith((t)=>semaphore.Release()));
}
await Task.WhenAll(tasks);

As aplicações nunca devem iniciar uma operação de envio assíncrona de maneira "lançar e esquecer" sem recuperar o resultado da operação. Isso pode carregar a fila de tarefas interna e invisível até o esgotamento da memória e impedir que o aplicativo detete erros de envio:

for (int i = 0; i < 10; i++)
{
    sender.SendMessageAsync(message); // DON’T DO THIS
}

Com um cliente AMQP de baixo nível, o Service Bus também aceita transferências "pré-estabelecidas". Uma transferência pré-liquidada é uma operação em que não se espera resposta, para a qual o resultado, independentemente do que seja, não é relatado de volta ao cliente e a mensagem é considerada finalizada quando enviada. A falta de comentários ao cliente também significa que não há dados acionáveis disponíveis para diagnóstico, o que significa que esse modo não se qualifica para ajuda por meio do suporte do Azure.

Operações de liquidação de receção

Para operações de recebimento, os clientes da API do Service Bus habilitam dois modos explícitos diferentes: Receber e Excluir e Peek-Lock.

ReceiveAndDelete

O modo Receber e Excluir diz ao corretor de mensagens para que considere todas as mensagens que envia ao cliente recetor como finalizadas assim que são enviadas. Isso significa que a mensagem é considerada consumida assim que o corretor a coloca na rede. Se a transferência da mensagem falhar, a mensagem será perdida.

A vantagem deste modo é que o recetor não precisa tomar mais medidas sobre a mensagem e também não é retardado por esperar pelo resultado da liquidação. Se os dados contidos nas mensagens individuais tiverem baixo valor e/ou forem significativos apenas por um período de tempo muito curto, este modo é uma escolha razoável.

PeekLock

O modo Peek-Lock informa ao broker que o cliente receptor deseja confirmar as mensagens recebidas explicitamente. A mensagem é disponibilizada para o recetor processar, enquanto mantida sob um bloqueio exclusivo no serviço para que outros recetores concorrentes não possam vê-la. A duração do bloqueio é inicialmente definida no nível da fila ou da assinatura e pode ser estendida pelo cliente proprietário do bloqueio, por meio da operação RenewMessageLockAsync . Para obter detalhes sobre a renovação de bloqueios, consulte a seção Renovar bloqueios neste artigo.

Quando uma mensagem é bloqueada, outros clientes que recebem da mesma fila ou assinatura podem assumir bloqueios e recuperar as próximas mensagens disponíveis que não estão sob bloqueio ativo. Quando o bloqueio de uma mensagem é explicitamente removido ou quando o bloqueio expira, a mensagem é colocada na frente ou perto da frente da ordem de recuperação para nova entrega.

Quando a mensagem é repetidamente liberada pelos recetores ou eles deixam o bloqueio expirar por um número definido de vezes (Max Delivery Count), a mensagem é automaticamente removida da fila ou assinatura e colocada na fila de dead-letter associada.

O cliente recetor inicia a confirmação de uma mensagem recebida com um reconhecimento positivo ao chamar a API Complete para a mensagem. Ele indica ao agente que a mensagem foi processada com êxito e a mensagem foi removida da fila ou assinatura. O corretor responde à intenção de liquidação do destinatário com uma resposta que indica se a liquidação pode ser realizada.

Quando o cliente recetor não consegue processar uma mensagem, mas deseja que a mensagem seja entregue novamente, ele pode pedir explicitamente que a mensagem seja liberada e desbloqueada instantaneamente chamando a API Abandon para a mensagem ou não pode fazer nada e deixar o bloqueio passar.

Se um cliente recetor não conseguir processar uma mensagem e souber que reentregar a mensagem e tentar novamente a operação não ajudará, ele poderá rejeitar a mensagem, o que a move para a fila de mensagens mortas chamando a API DeadLetter na mensagem, o que também permite definir uma propriedade personalizada, incluindo um código de motivo que pode ser recuperado com a mensagem da fila de mensagens mortas.

Nota

Uma subfila de mensagens mortas existe para uma fila ou uma assinatura de tópico somente quando o utilizador tem o recurso de mensagens mortas habilitado para a fila ou assinatura.

Um caso especial de liquidação é o diferimento. Consulte o Adiamento de mensagem para obter detalhes.

As Completeoperações , DeadLetterou RenewLock podem falhar devido a problemas de rede, se o bloqueio mantido tiver expirado ou se houver outras condições do lado do serviço que impeçam a liquidação. Em um dos últimos casos, o serviço envia uma confirmação negativa que surge como uma exceção nos clientes de API. Se o motivo for uma conexão de rede quebrada, o bloqueio será descartado, pois o Service Bus não oferece suporte à recuperação de links AMQP existentes em uma conexão diferente.

Se Complete falhar, o que ocorre normalmente no final do processamento de mensagens e, em alguns casos, após minutos de trabalho de processamento, o aplicativo recetor pode decidir se preserva o estado do trabalho e ignora a mesma mensagem quando ela é entregue uma segunda vez, ou se deseja descartar o resultado do trabalho e repetir as tentativas à medida que a mensagem é entregue novamente.

O mecanismo típico para identificar entregas de mensagens duplicadas é verificando o message-id, que pode e deve ser definido pelo remetente para um valor único, possivelmente alinhado com um identificador do processo de origem. Um agendador de tarefas provavelmente definiria o identificador message-id do trabalho que está tentando atribuir ao trabalhador designado, e o trabalhador ignoraria a segunda ocorrência da atribuição de trabalho se esse trabalho já estiver concluído.

Importante

É importante notar que o bloqueio que PeekLock ou SessionLock adquire na mensagem é volátil e pode ser perdido nas seguintes condições

  • Atualização do Serviço
  • Atualização do SO
  • Alterar propriedades na entidade (Fila, Tópico, Assinatura) enquanto se mantém o bloqueio.
  • Se o aplicativo cliente do Service Bus perder sua conexão com o Service Bus por qualquer motivo.

Quando o bloqueio é perdido, o Azure Service Bus gera um MessageLockLostException ou SessionLockLostException, que é exibido no aplicativo cliente. Neste caso, a lógica de repetição predefinida do cliente tem de ser acionada automaticamente e a operação será repetida. Além disso, a contagem de entrega da mensagem não é incrementada.

Renovar fechaduras

O valor padrão para a duração do bloqueio é 1 minuto. Você pode especificar um valor diferente para a duração do bloqueio no nível da fila ou da assinatura . O cliente que possui o bloqueio pode renovar o bloqueio de mensagem usando métodos no objeto recetor. Em vez disso, você pode usar o recurso de renovação automática de bloqueio, onde pode especificar a duração do tempo durante o qual deseja manter o bloqueio renovado.

É melhor definir a duração do bloqueio para algo maior do que o seu tempo de processamento normal, para que você não precise renovar o bloqueio. O valor máximo é de 5 minutos, então você precisa renovar o bloqueio se quiser tê-lo por mais tempo. Ter uma duração de bloqueio mais longa do que o necessário também tem algumas implicações. Por exemplo, quando o cliente parar de funcionar, a mensagem só ficará disponível novamente depois que a duração do bloqueio tiver passado.

Próximos passos