Arquitetura real · Sênior
Netflix Zuul 2: gateway assíncrono
Arquitetura real em produção. Pontos centrais: WebSocket e presença.
Resposta curta
Arquitetura real em produção. Pontos centrais: WebSocket e presença. Segundo o post da equipe de Cloud Gateway do Netflix, o Zuul 2 faz o mesmo que o antecessor: atua como porta de entrada da infraestrutura de servidores, recebendo o tráfego dos usuários. A diferença arquitetural principal é rodar sobre um framework assíncrono e não bloqueante, o Netty.
01 · CONTEXTO
O gateway que é a porta de entrada do Netflix
Segundo o post da equipe de Cloud Gateway do Netflix, o Zuul 2 faz o mesmo que o antecessor: atua como porta de entrada da infraestrutura de servidores, recebendo o tráfego dos usuários. A diferença arquitetural principal é rodar sobre um framework assíncrono e não bloqueante, o Netty.
O benefício principal, esperado desde o início, é permitir conexões persistentes de dispositivos e navegadores com o Netflix na escala do Netflix. O post cita mais de 83 milhões de membros, cada um com vários dispositivos conectados, e chama isso de um desafio de escala massivo.
Na entrevista ao InfoQ, a equipe explica o objetivo: o polling responde por uma parcela significativa das requisições dos dispositivos aos microsserviços, e conexões persistentes permitem trocá-lo por notificações push, além de comunicação bidirecional entre dispositivos e sistemas de controle na nuvem.
02 · BLOQUEANTE VERSUS ASSÍNCRONO
Uma thread por conexão ou um laço de eventos
O Zuul 1 foi construído sobre o framework Servlet. Esses sistemas são bloqueantes e multithread: processam as requisições com uma thread por conexão. O diagrama mostra o que o post descreve quando a latência do backend aumenta.
Cada conexão de dispositivo ocupa uma thread do gateway bloqueante.
A origem passa a responder mais devagar.
Conexões e threads ativas aumentam, inclusive quando dispositivos repetem requisições por causa de erros.
Nesse ponto o nó pode entrar numa espiral da morte, em que threads acumuladas elevam a carga e sobrecarregam o cluster.
Os sistemas assíncronos funcionam de outro jeito: usam em geral uma thread por núcleo de CPU para todas as requisições e respostas, e o ciclo da requisição é tratado por eventos e callbacks. Como não há uma thread por requisição, o custo de uma conexão é o de um descritor de arquivo e de um listener, enquanto no modelo bloqueante o custo de uma conexão é uma thread, com sobrecarga pesada de memória e de sistema.
Poucas threads atendem todas as conexões, e o ciclo de cada requisição é conduzido por eventos e callbacks.
Abrir mais uma conexão custa o de um descritor de arquivo e de um listener, não o de uma thread.
A origem fica mais lenta, como no cenário bloqueante.
Mais conexões e eventos na fila custam bem menos do que threads empilhadas, e por isso o gateway sente menos o impacto.
A equipe ressalva que a vantagem de resiliência não veio sem esforço e ajuste.
| Aspecto | Bloqueante | Assíncrono |
|---|---|---|
| Threads | Uma thread por conexão. | Em geral uma thread por núcleo de CPU para todas as requisições e respostas. |
| Custo de uma conexão | Uma thread, com sobrecarga pesada de memória e de sistema. | O custo de um descritor de arquivo e de um listener. |
| Latência do backend e retentativas | Conexões e threads ativas aumentam, e o nó pode entrar numa espiral da morte. | Conexões e eventos na fila custam bem menos do que threads empilhadas. |
| Depuração | Fácil de entender e depurar. | O stack trace do laço de eventos não ajuda a seguir uma requisição. |
03 · FILTROS
O caminho da requisição pelo gateway
Segundo a documentação do projeto, o Zuul 2.0 é um servidor Netty que executa filtros de entrada, faz proxy da requisição com um cliente Netty e devolve a resposta depois dos filtros de saída.
A requisição do dispositivo entra no servidor e passa primeiro pelos filtros de entrada.
Os filtros de entrada executam antes do roteamento para a origem.
O endpoint devolve uma resposta estática ou, por padrão, roteia a requisição para a origem.
Quando a resposta da origem chega, os filtros de saída entram em ação.
A resposta volta ao dispositivo depois dos filtros de saída.
Como o servidor roda num laço de eventos, a documentação afirma que é crítico nunca bloquear num filtro. Se for preciso bloquear, ela manda fazê-lo num filtro assíncrono, em um pool de threads separado.
04 · MIGRAÇÃO
Como o Netflix chegou ao gateway assíncrono
A equipe começou pelos filtros. Como sistemas bloqueantes podem rodar código assíncrono, primeiro mudou os Zuul Filters e o encadeamento deles para rodarem de forma assíncrona. Assim, a mesma lógica de filtros executa num sistema bloqueante e num não bloqueante. No InfoQ, a equipe descreve isso como um investimento inicial grande, de cerca de seis meses ou mais, pré-requisito para construir o Zuul 2.
Hoje há dois tipos de filtro usados em conjunto: assíncronos, para operações de I/O, e síncronos, para operações lógicas que não exigem I/O. Segundo o InfoQ, a equipe usou RxJava para encadear os filtros.
Muitos serviços do ecossistema Netflix foram construídos supondo bloqueio. Muitas bibliotecas usam variáveis thread local para guardar o contexto da requisição, e isso não funciona quando várias requisições são processadas na mesma thread. Boa parte da complexidade de construir o Zuul 2 foi descobrir onde essas variáveis eram usadas.
Na entrevista, a equipe diz que o papel do gateway é tratar principalmente roteamento, insights e normalização das requisições que chegam. Um desvio no caminho foi remover muita lógica de negócio do Zuul e levá-la aos sistemas de origem, o que também simplificou a migração do código bloqueante.
05 · CONEXÕES PERSISTENTES
De polling a push
O post diz que o Zuul se beneficia de reduzir bastante o custo das conexões de rede, o que habilita comunicação push e bidirecional de e para os dispositivos. A equipe espera que trocar protocolos verbosos dos dispositivos por notificações push reduza o custo geral de nuvem; esses protocolos respondem por parte significativa do tráfego da API.
Os dispositivos consultam os microsserviços por polling, que o post diz responder por parcela significativa das requisições dos dispositivos aos microsserviços.
Com o gateway assíncrono, dispositivos e navegadores podem manter conexões persistentes com a nuvem na escala do Netflix.
Com a conexão persistente, um sistema de controle na nuvem pode enviar uma mensagem ao dispositivo por push, em vez de o dispositivo consultar por polling.
A equipe planejava acrescentar suporte a http/2 e websocket ao gateway.
O post informa que a equipe planejava acrescentar suporte a http/2 e websocket ao Zuul 2.
06 · RESULTADOS
O que a mudança entregou
Os resultados foram mistos. Segundo o post, não houve ganho significativo de eficiência ao migrar para async e não bloqueante, mas as metas de escala de conexões foram atingidas.
No InfoQ, a equipe diz ver melhoria de eficiência de 10-25% em alguns clusters, não em todos, mas ficou decepcionada com essa área, e atribui o erro de cálculo à quantidade de trabalho de CPU feito dentro do gateway.
| Cluster | Trabalho no gateway | Resultado do async |
|---|---|---|
| API | Maior trabalho local entre os clusters: métricas, logs, decifrar payloads de entrada e comprimir respostas. | Sem ganho de eficiência; capacidade e CPU essencialmente equivalentes. |
| Logging | Requisições grandes e respostas pequenas, não criptografadas pelo Zuul. | Cerca de 25% mais vazão com 25% menos uso de CPU. |
A conclusão da equipe é que, quanto mais limitado por CPU é um sistema, menor o ganho de eficiência, e que, quanto menos trabalho o sistema faz, mais eficiência se ganha com async.
Perguntas frequentes
O que é Netflix Zuul 2: gateway assíncrono?
Arquitetura real em produção. Pontos centrais: WebSocket e presença.
O que o design de Netflix Zuul 2: gateway assíncrono cobre?
Ele está organizado nestas seções: O gateway que é a porta de entrada do Netflix, Uma thread por conexão ou um laço de eventos, O caminho da requisição pelo gateway, Como o Netflix chegou ao gateway assíncrono, De polling a push e O que a mudança entregou.
Quanto tempo leva Netflix Zuul 2: gateway assíncrono e qual é o nível?
Leva cerca de 17 minutos para ler e tem nível Sênior.
Agora projete você mesmo
Rode este caso como uma entrevista na escola de System Design: você toma as decisões, o tutor as questiona e você vê a nota do seu desenho.