Implementar Fallback com API Gateway
November 10, 2023
A resiliência da API é a capacidade de uma API de falhar rapidamente ou garantir que continue a funcionar após uma falha quando enfrenta erros, tráfego elevado ou falhas parciais do sistema. Isso envolve a implementação de padrões comuns de design de resiliência de API, como retentativas, timeouts, circuit breakers, failover e fallbacks. Um fallback usando um API Gateway é um plano B para uma API - quando o serviço primário da API falha, o API Gateway pode redirecionar o tráfego para um serviço secundário ou retornar uma resposta predefinida. Neste artigo, exploraremos os desafios das técnicas de fallback existentes e como implementá-las de forma eficiente usando o APISIX API Gateway.
Implementando Fallbacks com APISIX
Para implementar um mecanismo de fallback com o APISIX, você pode usar seu recurso interno de prioridades de upstream ou usar um plugin de response-rewrite para retornar uma resposta predefinida quando uma chamada de serviço falha. Aqui está um exemplo passo a passo de como configurar ambos os métodos de fallback.
Pré-requisito(s)
Este guia assume que as seguintes ferramentas estão instaladas localmente:
- Antes de começar, é bom ter um entendimento básico do APISIX. Familiaridade com API gateway e seus conceitos-chave, como rotas, upstream, Admin API, plugins e protocolo HTTP também será benéfico.
- Docker é usado para instalar o etcd e o APISIX em contêineres.
- Instale o cURL para enviar solicitações aos serviços para validação.
Iniciando o Projeto Docker do APISIX
Este projeto aproveita o arquivo de configuração Docker Compose predefinido para configurar, implantar e executar o APISIX, etcd, Prometheus e outros serviços com um único comando. Primeiro, clone o repositório apisix-docker no GitHub, abra-o no seu editor favorito, navegue até a pasta example
e inicie o projeto simplesmente executando o comando docker compose up
em um terminal a partir da pasta raiz do projeto.
Quando você inicia o projeto, o Docker baixa quaisquer imagens necessárias para executar. Ele também executa dois serviços de backend de exemplo, web1
e web2
. Você pode ver a lista completa de serviços no arquivo docker-compose.yaml.
Fallback com Prioridades de Upstream do APISIX Habilitadas
Você pode configurar cada nó upstream com um certo nível de prioridade para habilitar. Quando o endpoint do nó com a prioridade mais alta falha, o API Gateway pode redirecionar o tráfego para um nó secundário com uma prioridade mais baixa. A prioridade padrão para todos os nós é 0, e nós com prioridade negativa podem ser configurados como backup.
Crie uma rota para os dois serviços e configure o atributo de prioridade para cada nó do serviço upstream:
curl "http://127.0.0.1:9180/apisix/admin/routes" -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -d '
{
"id":"backend-service-route",
"methods":[
"GET"
],
"uri":"/get",
"upstream":{
"nodes":[
{
"host":"web1",
"port":80,
"weight":1,
"priority":0
},
{
"host":"web2",
"port":80,
"weight":1,
"priority":-1
}
]
}
}'
methods
: Especifica o método HTTP que esta rota corresponderá. Neste caso, está configurado para corresponder a solicitaçõesGET
.uri
: Este é o caminho que a rota corresponderá. Portanto, qualquer solicitaçãoGET
para/get
será tratada por esta rota.nodes
: Este é um array de servidores backend. Cada objeto no array representa um servidor com seuhost
,port
eweight
. Oweight
é usado para balanceamento de carga; neste caso, ambos os servidores têm um peso de1
, o que normalmente significa que compartilhariam o tráfego igualmente.priority
: Esta é uma configuração adicional para os dois nós (web1
,web2
). O campopriority
é usado para determinar a ordem em que os nós são selecionados. Um nó com uma prioridade mais baixa (um número negativo maior) será usado apenas se todos os nós com prioridade mais alta (números negativos menores ou números positivos) estiverem indisponíveis.
Verifique se você obtém apenas uma resposta do serviço web1
ao enviar uma solicitação para a rota:
curl "http://127.0.0.1:9080/get"
Você deve ver uma resposta semelhante à seguinte:
hello web1
Isso significa que web1
foi executado primeiro, pois está funcionando. Agora, pare o contêiner do serviço web1
para verificar se o APISIX faz fallback para o serviço web2
.
docker container stop example-web1-1
Agora, se você enviar outra solicitação para a rota, obterá uma resposta do serviço de fallback que especificamos.
curl "http://127.0.0.1:9080/get"
hello web2
Por padrão, leva 60 segundos enquanto a solicitação vai primeiro para o serviço um e faz fallback para o serviço dois se ele estiver indisponível. Você também pode alterar esse tempo configurando o atributo timeout
do objeto Upstream. Outra estratégia de fallback poderia ser durante lançamentos. Se uma nova versão da API estiver com bugs, você pode rotear o tráfego para a versão antiga que está em standby usando o recurso de divisão de tráfego do APISIX. Faça fallback para a versão anterior se a nova versão tiver problemas. Este método de fallback também funciona bem com verificações de saúde do Upstream.
Fallback com o Plugin de Reescrever Resposta do APISIX
O plugin response-rewrite do APISIX permite modificar o código de status da resposta, o corpo e os cabeçalhos antes de retorná-los ao cliente. Isso pode ser particularmente útil para implementar um mecanismo de fallback, fornecendo uma resposta padrão quando o serviço upstream falha.
Se você seguiu a primeira abordagem, reinicie o contêiner do serviço web1
no Docker:
docker container start example-web1-1
Para usar o plugin response-rewrite para um fallback, você precisa configurá-lo em uma rota. Aqui está um exemplo de como você pode habilitar o plugin usando um comando curl
:
curl "http://127.0.0.1:9180/apisix/admin/routes" -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -d '
{
"id":"backend-service-route",
"methods":[
"GET"
],
"uri":"/get",
"plugins":{
"response-rewrite":{
"status_code":200,
"body":"{\"message\":\"This is a fallback response when the service is unavailable\"}",
"vars":[
[
"status",
"==",
503
]
]
}
},
"upstream":{
"nodes":{
"web1:80":1
}
}
}'
No exemplo acima, definimos um único serviço backend (web1:80
) para onde o tráfego deve ser direcionado quando esta rota for correspondida. Se o serviço upstream (web1:80
) responder com um status 503 Service Unavailable
, o plugin response-rewrite
modificará a resposta para ter um status 200 OK
com um corpo JSON personalizado. Isso efetivamente cria uma resposta de fallback quando o serviço upstream não está disponível.
"vars": [["status", "==", 503]]
: Esta condição informa ao plugin para aplicar a reescrita apenas quando o código de status original da resposta for503 Service Unavailable
.
Agora, se você enviar uma solicitação para a rota, deverá obter uma resposta modificada:
curl "http://127.0.0.1:9080/get"
{"message":"This is a fallback response when the service is unavailable"}
Desafios da Implementação de um Mecanismo de Fallback
Fallbacks são um componente crítico de um design de sistema resiliente. No entanto, eles podem introduzir mais problemas quando implementados incorretamente. Ao discutir estratégias de fallback, os desafios enfrentados podem diferir entre ambientes de máquina única e sistemas distribuídos. Vamos revisá-los para entendê-los com exemplos e aprender como evitá-los usando o APISIX.
Dificuldade em Testar a Lógica de Fallback
É difícil simular com precisão condições de falha do aplicativo, como uma falha de banco de dados, em um contexto de máquina única. Testar estratégias de fallback em sistemas distribuídos se torna ainda mais complexo devido ao envolvimento de várias máquinas e serviços, tornando difícil replicar todos os modos de falha possíveis. Por exemplo, uma API em um servidor local tem um fallback para uma resposta em cache quando o banco de dados está inacessível. Testar esse cenário requer simular uma indisponibilidade do banco de dados, o que pode não fazer parte dos testes regulares, levando a um código de fallback não testado sob carga real de produção.
O APISIX pode ser configurado para rotear tráfego para simular vários cenários, incluindo condições de fallback. Isso permite testes mais realistas da lógica de fallback sob condições controladas, garantindo que os serviços de fallback possam lidar com o tráfego de produção.
Fallbacks podem falhar
Se uma solução de fallback não for tão resiliente quanto o esperado, ela pode falhar sob a carga aumentada que ocorre quando é acionada, causando uma falha em cascata. Além disso, um fallback para um serviço menos eficiente pode aumentar os tempos de resposta e a carga, potencialmente levando a uma desaceleração ou interrupção em todo o sistema. Por exemplo, uma API pode ter um fallback para gravar logs no sistema de arquivos local quando um serviço de log remoto estiver indisponível. Isso pode levar a um desempenho mais lento devido a operações de I/O de arquivo síncronas.
Com o APISIX, você pode priorizar o tráfego para garantir que solicitações críticas sejam processadas primeiro. Isso pode impedir que um serviço de fallback fique sobrecarregado e piore o desempenho do sistema.
Fallbacks têm riscos operacionais
A implementação de um fallback pode introduzir novos pontos de falha, como um banco de dados secundário que não é mantido em sincronia com o primário, levando a inconsistências de dados.
Os recursos de observabilidade do APISIX, como logs, métricas e rastreamento, podem monitorar a saúde e o desempenho de serviços primários e de fallback. Esse monitoramento em tempo real pode ajudar a identificar e mitigar riscos associados a estratégias de fallback.
Fallbacks têm bugs latentes e amplificados
Caminhos de código de fallback podem conter bugs inativos que só ocorrem sob condições específicas de falha, que podem não acontecer com frequência e são difíceis de prever, podendo não ser descobertos por meses ou anos. Por exemplo, um mecanismo de fallback em uma API que muda para um método de autenticação diferente durante uma indisponibilidade do serviço de identidade pode conter um bug que só aparece quando o fallback é acionado, o que pode ser um evento raro.
O APISIX suporta testes A/B contínuos e lançamentos canários, permitindo que as equipes testem caminhos de fallback em produção com uma pequena porcentagem do tráfego. Essa exposição contínua pode ajudar a descobrir bugs latentes antes que se tornem críticos.
Fallbacks são pouco exercitados
Mecanismos de fallback são pouco usados, então, quando são acionados, podem não funcionar como esperado devido à falta de testes e atualizações regulares. Por exemplo, uma API que serve dados geográficos pode ter um fallback para um conjunto de dados estático quando a fonte de dados dinâmicos estiver indisponível. Se esse fallback for raramente usado, ele pode servir informações desatualizadas quando ativado, porque não é atualizado ou testado regularmente.
Por outro lado, o APISIX permite a configuração de roteamento dinâmico e pode ser usado para redirecionar periodicamente uma parte do tráfego para os serviços de fallback. Isso garante que os caminhos de fallback sejam exercitados regularmente e permaneçam prontos para uso.
Conclusão
Fallbacks são uma rede de segurança para quando as coisas dão errado com as APIs. Ao usar a configuração de upstream do APISIX ou o plugin de reescrita de resposta, os desenvolvedores podem fornecer respostas cuidadosas e amigáveis ao usuário que mantêm o sistema funcional e mantêm a confiança dos usuários. A chave é antecipar possíveis pontos de falha e projetar fallbacks que forneçam a melhor experiência possível sob as circunstâncias.