Exportar (0) Imprimir
Expandir Tudo

Tratando erros de comunicação transitórios

Atualizado: março de 2014

Para aprimorar a confiabilidade de uma solução que use a API do sistema de mensagens orientadas gerenciada pelo Microsoft Azure Service Bus .NET, é recomendável que você adote uma abordagem consistente para lidar com falhas transitórias e com erros intermitentes que possam aparecer quando a solução se comunicar com a infraestrutura de serviços do serviço de mensagens de publicação/assinatura e de enfileiramento baseados em nuvem com vários locatários fornecida pelo Service Bus.

Para aprimorar a confiabilidade de uma solução que use a API do sistema de mensagens orientadas gerenciada pelo Service Bus .NET, é recomendável que você adote uma abordagem consistente para lidar com falhas transitórias e com erros intermitentes que possam aparecer quando a solução se comunicar com a infraestrutura de serviço do sistema de mensagens de publicação/assinatura e de enfileiramento baseados em nuvem com vários locatários fornecida pelo Service Bus.

Ao considerar uma técnica específica para a detecção de condições transitórias, talvez você queira reutilizar soluções técnicas existentes, como a Estrutura de Tratamento de Falhas Transitórias, ou criar a sua própria solução. Em ambos os casos, você deverá garantir que somente um subconjunto de exceções de comunicação será tratado como transitório antes de tentar se recuperar das respectivas falhas.

A tabela mostrada aqui oferece uma lista de exceções que podem ser compensadas pela implementação de lógica de repetição:

 

Tipo de exceção

Recomendação

ServerBusyException

Esta exceção pode ser causada por uma falha intermitente na infraestrutura de serviço do sistema de mensagens do Service Bus que não é capaz de processar uma solicitação por causa de condições anormais de carga pontuais. O cliente pode tentar repetir com um atraso. Seria preferível um atraso recuado para impedir uma pressão desnecessária no servidor.

MessagingCommunicationException

Esta exceção sinaliza um erro de comunicação que pode se manifestar quando não é possível estabelecer uma conexão do cliente de mensagem com a infraestrutura do Service Bus. Na maioria dos casos, desde que haja conectividade de rede, esse erro pode ser tratado como transitório. O cliente pode tentar repetir a operação que resultou neste tipo de exceção. Também é recomendável que você verifique se o serviço de resolução de nomes de domínio (DNS) está funcionando, uma vez que o erro pode indicar que o nome de host de destino não pode ser resolvido.

TimeoutException

Esta exceção indica que a infraestrutura de serviço do sistema de mensagens do Service Bus não respondeu à operação solicitada no tempo especificado e que é controlado pela configuração OperationTimeout. A operação solicitada ainda pode ter sido concluída; entretanto, por causa de atrasos de rede ou de outra infraestrutura, a resposta pode não ter chegado ao cliente a tempo. A compensação desse tipo de exceção deve ser feita com cuidado. Se uma mensagem tiver sido entregue em uma fila, mas uma resposta atingiu o tempo limite, o reenvio da mensagem original causará a duplicação.

Para obter informações mais detalhadas sobre diferentes tipos de exceção que podem ser relatados pela API do sistema de mensagens do Service Bus, consulte o tópico Exceções do sistema de mensagens.

noteObservação
Ao tratar erros de comunicação transitórios, tenha cuidado com exceções transitórias mascaradas por exceções externas de um tipo diferente. Por exemplo, um tempo limite pode retornar ao chamador na forma de um erro de comunicação que oculte o tempo limite original como uma exceção interna. Nesse caso, é recomendável que você inspecione todas as exceções internas de um determinado objeto de exceção de forma recursiva para poder detectar com segurança erros de comunicação transitórios. A classe ServiceBusTransientErrorDetectionStrategy da Estrutura de Tratamento de Falhas Transitórias oferece um exemplo de como fazer isso.

O trecho de código a seguir demonstra como enviar uma mensagem de forma assíncrona para um tópico do Service Bus e, ao mesmo tempo, garantir que todas as falhas transitórias sejam compensadas em uma repetição. Observe que esse código de exemplo mantém uma dependência da Estrutura de Tratamento de Falhas Transitórias.

var credentials = TokenProvider.CreateSharedSecretTokenProvider(issuerName, issuerSecret);
var address = ServiceBusEnvironment.CreateServiceUri("sb", serviceNamespace, String.Empty);
var messagingFactory = MessagingFactory.Create(address, credentials);
var topicClient = messagingFactory.CreateTopicClient(topicPath);
var retryPolicy = new RetryPolicy<ServiceBusTransientErrorDetectionStrategy>(RetryPolicy.DefaultClientRetryCount);

// Create an instance of the object that represents message payload.
var payload = XDocument.Load("InventoryFile.xml");

// Declare a BrokeredMessage instance outside so that it can be reused across all 3 delegates below.
BrokeredMessage msg = null;

// Use a retry policy to execute the Send action in an asynchronous and reliable fashion.
retryPolicy.ExecuteAction
(
    (cb) =>
    {
        // A new BrokeredMessage instance must be created each time we send it. Reusing the original BrokeredMessage instance may not 
        // work as the state of its BodyStream cannot be guaranteed to be readable from the beginning.
        msg = new BrokeredMessage(payload.Root, new DataContractSerializer(typeof(XElement)));

        // Send the event asynchronously.
        topicClient.BeginSend(msg, cb, null);
    },
    (ar) =>
    {
        try
        {
            // Complete the asynchronous operation. 
            // This may throw an exception that will be handled internally by the retry policy.
            topicClient.EndSend(ar);
        }
        finally
        {
            // Ensure that any resources allocated by a BrokeredMessage instance are released.
            if (msg != null)
            {
                msg.Dispose();
                msg = null;
            }
        }
    },
    (ex) =>
    {
        // Always dispose the BrokeredMessage instance even if the send 
        // operation has completed unsuccessfully.
        if (msg != null)
        {
            msg.Dispose();
            msg = null;
        }

        // Always log exceptions.
        Trace.TraceError(ex.Message);
    }
);

O próximo código de exemplo mostra como criar com segurança um novo tópico do Service Bus ou como recuperar um existente. Este código também mantém uma dependência na Estrutura de Tratamento de Falhas Transitórias que repetirá automaticamente a operação de gerenciamento correspondente caso ela não consiga ser concluída com êxito por causa de problemas intermitentes de conectividade ou de outros tipos de condições transitórias:

public TopicDescription GetOrCreateTopic(string issuerName, string issuerSecret, string serviceNamespace, string topicName)
{
    // Must validate all input parameters here. Use Code Contracts or build your own validation.
    var credentials = TokenProvider.CreateSharedSecretTokenProvider(issuerName, issuerSecret);
    var address = ServiceBusEnvironment.CreateServiceUri("sb", serviceNamespace, String.Empty);
    var nsManager = new NamespaceManager(address, credentials);
    var retryPolicy = new RetryPolicy<ServiceBusTransientErrorDetectionStrategy>(RetryPolicy.DefaultClientRetryCount);

    TopicDescription topic = null;
    bool createNew = false;

    try
    {
        // First, let's see if a topic with the specified name already exists.
        topic = retryPolicy.ExecuteAction<TopicDescription>(() => { return nsManager.GetTopic(topicName); });

        createNew = (topic == null);
    }
    catch (MessagingEntityNotFoundException)
    {
        // Looks like the topic does not exist. We should create a new one.
        createNew = true;
    }

    // If a topic with the specified name doesn't exist, it will be auto-created.
    if (createNew)
    {
        try
        {
            var newTopic = new TopicDescription(topicName);

            topic = retryPolicy.ExecuteAction<TopicDescription>(() => { return nsManager.CreateTopic(newTopic); });
        }
        catch (MessagingEntityAlreadyExistsException)
        {
            // A topic under the same name was already created by someone else, 
            // perhaps by another instance. Let's just use it.
            topic = retryPolicy.ExecuteAction<TopicDescription>(() => { return nsManager.GetTopic(topicName); });
        }
    }

    return topic;
}

Em resumo, recomendamos que você avalie a probabilidade da ocorrência de uma falha e determine a viabilidade de adicionar mais resiliência. Virtualmente todas as operações do sistema de mensagens estão sujeitas a condições transitórias. Ao chamar a API do sistema de mensagens orientadas, no entanto, é recomendável que você tome as ações apropriadas para sempre fornecer recuperação de problemas intermitentes.

Mostrar:
© 2014 Microsoft