Como tratar falhas em web services – Idempotent Retry Pattern (parte 1 de 2)

Livro Service Design Patterns - de Ian RobinsonOlá pessoal,

Nos dois posts anteriores vimos alguns padrões para requisição e reposta de serviços, como por exemplo, os padrões síncronos Request/Response e Asynchronous Response Handler e os padrões assíncronos Request/Acknowledge/Pool e Request/Acknowledge/Callback, que foram discutidos em detalhes. Em todos os padrões para requisição e resposta de serviços que vimos, podemos nos deparar com problemas de indisponibilidade, o que levará a uma falha que será percebida pela aplicação consumidora. Se tal falha não for tratada adequadamente, poderemos ter o nível de confiabilidade (reliability) da solução diminuído, além da possibilidade de termos impactos para o negócio.

Neste post veremos como endereçar esta questão através de um padrão para tratamento de falhas de disponibilidade de serviços, também descrito no livro Service Design Patterns, de Ian Robinson.

Infraestruturas em geral muito dificilmente estão disponíveis em 100% do tempo. Comumente encontramos níveis de disponibilidade estabelecidas por SLA’s de 99.9%, 99.8% e 98%, que implicam em 8,76, 17,52 e 175 horas de indisponibilidade por ano, respectivamente. Menos comuns, porém, são infraestruturas estruturadas para 99.99% ou 99.999%, que representam 52,56 e 5,25 minutos de indisponibilidade por ano, respectivamente. Diversos são os recursos envolvidos para se estabelecer uma infraestrutura de alta disponibilidade e o custo associado é proporcionalmente alto. Mesmo em infraestruturas de alta disponibilidade, como as que garantem 99.999%, ainda temos períodos de indisponibilidade acordados em contrato, que podem calhar no momento exato da chamada de um serviço hospedado em tal infraestrutura.

É preciso ainda considerar que mesmo serviços hospedados na nuvem estão sujeitos à impossibilidade de acesso, seja por uma indisponibilidade prevista em um SLA definido pelo próprio provedor da infraestrutura, ou por indisponibilidade de banda da organização consumidora, ou ainda do seu provedor de internet (ISP), por exemplo.

Neste contexto, como garantir que chamadas à web services (sejam síncronas ou assíncronas) sejam bem sucedidas, independentemente de indisponibilidades de infraestrutura (falhas de rede ou de servidores)? Veremos a seguir mais detalhes sobre as possíveis causas de falhas em requisições de serviços e posteriormente possíveis soluções.

Tipos de Falha

Toleráveis

Em determinadas situações, falhas em chamadas de serviços são toleráveis. Considere, por exemplo, um serviço de busca de restaurantes, acionado pela implementação de uma funcionalidade acessada diretamente pelo usuário. Caso a requisição a este serviço não seja bem-sucedida, o usuário poderá ser notificado, podendo, por sua vez, acionar a mesma funcionalidade novamente, até que uma eventual falha temporária seja corrigida e o resultado esperado seja retornado.

Falha no estabelecimento de conexão

Um consumidor de serviço pode não ser capaz de se conectar ao serviço seja por instabilidade de rede – redes possuem intrinsicamente características de baixa confiabilidade – ou indisponibilidade de servidor (aquele que hospeda o serviço, por exemplo). Tais problemas podem ser temporários – uma instabilidade retomada em poucos segundos – ou crônica – uma indisponibilidade de servidor mais séria, por exemplo, como danos à CPU ou ao disco, em uma infraestrutura não projetada para alta disponibilidade.

Perda de conexão

Um consumidor de serviço pode enviar uma requisição ao serviço com sucesso, porém, pode ter sua conexão perdida enquanto o serviço ainda está processando sua requisição. Se o consumidor do serviço considerar que houve uma falha e enviar a mesma requisição novamente, é possível que dados sejam duplicados no provedor do serviço, o que causaria inconsistências e diminuiria a confiabilidade da solução.

Sobrecarga de servidores

Servidores podem estar sobrecarregados de tempos em tempos, de forma que se tais eventos coincidirem com momentos em que os serviços hospedados por eles são requisitados, haverá a possibilidade de o servidor recusar o estabelecimento da conexão ou não ser capaz de processá-la.

Timeout

Seja por sobrecarga de servidores, ou por qualquer falha que faça com que o servidor destino demore mais para responder do que o normal, um timeout poderá ser lançado no consumidor do serviço. A configuração e um tempo condizente de timeout é de extrema importância, para que o consumidor do serviço não fique bloqueado indefinidamente em tais situações. É importante notar que quando um timeout é lançado no consumidor do serviço, não é possível estabelecer se a requisição foi processada com sucesso ou não pelo serviço, uma vez que o processamento pode ter sido bem-sucedido momentos após a perda da conexão.

Indisponibilidade de URI

Uma vez estabelecido o serviço em produção, tal problema é menos comum. Porém, existem cenários de implementação que podem potencializar situações como esta. Serviços que implementam o padrão REST, por exemplo, frequentemente retornam como resposta URI’s para outras funcionalidades / dados. Tais URI’s podem ou ser inválidas por um bug de implementação ou podem ainda não estar disponíveis no momento da sua requisição. Este último cenário é conhecido como “race condition”, onde o consumidor do serviço compete com o serviço em si. Se o cliente for mais rápido, então links providos na comunicação anterior podem ser inválidos. Porém, se o cliente aguardar ou tentar novamente, as mesmas URI’s serão válidas posteriormente.

Possíveis soluções

A utilização de um sistema de enfileiramento de mensagens, como Microsoft Message Queue ou WebSphere MQ, resolveria o problema, uma vez que tais sistemas implementam o que se chama de garantia de entrega. Com a garantia de entrega (ou guaranteed delivery), quando uma fila remota não está disponível ou acessível, as mensagens são guardadas em uma fila local até que a infraestrutura remota seja restabelecida. Uma vez que a infraestrutura de enfileiramento de mensagens remota esteja estabelecida novamente, as mensagens pendentes serão entregues, podendo ser consumidas pelo sistema destino.

O problema com esta abordagem é que ele requer um protocolo de comunicação específico e proprietário, o pode não ser viável ou acessível para qualquer sistema. Ou seja, a interoperabilidade provida por protocolos de comunicação estabelecidos sobre HTTP, como SOAP e REST, não é mantida em tal abordagem. Além disso, a exposição de sistemas de enfileiramento de mensagens diretamente através de um firewall corporativo para consumidores externos à organização pode implicar na necessidade de abertura de portas, o que junto com outras questões, podem implicar em riscos à segurança.

Como endereçar então falhas em requisições de web servies de forma adequada?

Este é tópico do próximo post, onde será detalhado o padrão Idempotent Retry Pattern.

Até lá!

Deixe um comentário

O seu endereço de e-mail não será publicado.