QUIC em ação: como o Uber o implementou para otimizar o desempenho

O protocolo QUIC é extremamente interessante de se assistir, por isso gostamos de escrever sobre ele. Mas se as publicações anteriores sobre o QUIC eram mais de natureza e material histórico (história local, se você quiser), hoje temos o prazer de publicar uma interpretação diferente - falaremos sobre a aplicação real do protocolo em 2019. E não se trata de uma pequena infraestrutura baseada em uma garagem condicional, mas do Uber, que funciona em quase todo o mundo. Como os engenheiros da empresa tomaram a decisão de usar o QUIC na produção, como testaram e o que viram após entrar na produção - sob o corte.
As imagens são clicáveis. Boa leitura!



O Uber é uma escala global, ou seja, 600 cidades de presença, em cada uma das quais o aplicativo depende inteiramente da Internet sem fio de mais de 4.500 operadoras móveis. Os usuários esperam que o aplicativo funcione não apenas rápido, mas em tempo real - para garantir isso, o aplicativo Uber precisa de baixa latência e uma conexão muito confiável. Infelizmente, a pilha HTTP / 2 não se sente bem em redes sem fio dinâmicas e propensas a perdas. Percebemos que, nesse caso, o baixo desempenho está diretamente relacionado às implementações de TCP nos kernels dos sistemas operacionais.

Para solucionar o problema, aplicamos o QUIC , um protocolo moderno com multiplexação de canais, que nos dá mais controle sobre o desempenho do protocolo de transporte. O grupo de trabalho IETF está atualmente padronizando QUIC como HTTP / 3 .

Após testes detalhados, chegamos à conclusão de que a implementação do QUIC em nosso aplicativo reduzirá os atrasos "finais" em comparação ao TCP. Observamos uma redução na faixa de 10 a 30% para o tráfego HTTPS no exemplo de aplicativos de motorista e passageiro. O QUIC também nos deu controle de ponta a ponta sobre pacotes personalizados.

Neste artigo, compartilhamos nossa experiência na otimização de aplicativos TCP para Uber usando uma pilha que suporta QUIC.

Última palavra da tecnologia: TCP


Hoje, o TCP é o protocolo de transporte mais usado para fornecer tráfego HTTPS na Internet. O TCP fornece um fluxo confiável de bytes, lidando com o congestionamento da rede e a perda da camada de link. O uso generalizado do TCP para tráfego HTTPS é explicado pela onipresença do antigo (quase todos os sistemas operacionais contêm TCP), disponibilidade na maior parte da infraestrutura (por exemplo, em balanceadores de carga, proxies HTTPS e CDNs) e funcionalidade pronta para uso, que está disponível na maioria das plataformas e redes.

A maioria dos usuários usa nosso aplicativo em qualquer lugar, e os atrasos de TCP estão longe dos requisitos em tempo real do tráfego HTTPS. Simplificando, usuários de todo o mundo enfrentaram isso - a Figura 1 mostra atrasos nas grandes cidades:


Figura 1. A magnitude dos atrasos na cauda varia nas principais cidades onde a Uber opera.

Apesar de haver mais atrasos nas redes indianas e brasileiras do que nos EUA e na Grã-Bretanha, os atrasos são muito maiores que os atrasos médios. E isso é verdade mesmo para os EUA e a Grã-Bretanha.

Desempenho TCP over the air


O TCP foi criado para redes com fio , ou seja, com ênfase em links bem previsíveis. No entanto, as redes sem fio têm características e dificuldades próprias. Primeiro, as redes sem fio são suscetíveis à perda devido à interferência e atenuação do sinal. Por exemplo, as redes Wi-Fi são sensíveis a microondas, bluetooth e outras ondas de rádio. As redes celulares sofrem com perda de sinal (perda de caminho ) devido à reflexão / absorção do sinal por objetos e edifícios, bem como interferência de torres de células vizinhas. Isso leva a mais significantes (4-10 vezes) e uma variedade de atrasos de ida e volta (RTT) e perda de pacotes em comparação com uma conexão com fio.

Para combater flutuações e perdas de largura de banda, as redes celulares geralmente usam buffers grandes para rajadas de tráfego. Isso pode levar a uma prioridade excessiva, o que significa atrasos maiores. Muitas vezes, o TCP trata uma sequência como perda devido a um aumento do tempo limite, de modo que o TCP está inclinado a fazer um relé e, assim, preencher o buffer. Esse problema é conhecido como bufferbloat ( buffer excessivo de rede, aumento de buffer ) e é um problema muito sério da Internet moderna.

Finalmente, o desempenho da rede celular varia de acordo com a operadora, região e tempo. Na Figura 2, coletamos os atrasos medianos do tráfego HTTPS nas células em um intervalo de 2 quilômetros. Os dados são coletados para as duas maiores operadoras de telefonia móvel de Delhi, na Índia. Como você pode ver, o desempenho varia de célula para célula. Além disso, o desempenho de um operador é diferente do desempenho do segundo. Isso é influenciado por fatores como padrões de acesso à rede, levando em consideração o tempo e o local, a mobilidade do usuário e a infraestrutura da rede, levando em consideração a densidade das torres e a proporção dos tipos de rede (LTE, 3G, etc.).


Figura 2. Atrasos para um exemplo de raio de 2 km. Delhi, Índia.

Além disso, o desempenho das redes celulares varia ao longo do tempo. A Figura 3 mostra o atraso médio por dia da semana. Também observamos uma diferença em menor escala - dentro de um dia e uma hora.


Figura 3. Atrasos na cauda podem variar significativamente em dias diferentes, mas com o mesmo operador.

Tudo isso leva ao fato de que o desempenho do TCP é ineficiente em redes sem fio. No entanto, antes de procurar alternativas ao TCP, queríamos desenvolver um entendimento preciso dos seguintes pontos:
  • O TCP é o principal culpado por atrasos nas nossas aplicações?
  • As redes modernas têm atrasos significativos e variados de ida e volta (RTT)?
  • Qual é o efeito da perda de desempenho de RTT e TCP?

Análise de desempenho TCP


Para entender como analisamos o desempenho do TCP, vamos relembrar brevemente como o TCP transfere dados de um remetente para um destinatário. Primeiro, o remetente estabelece uma conexão TCP executando um handshake de três vias: o remetente envia um pacote SYN, aguarda um pacote SYN-ACK do destinatário e envia um pacote ACK. Segunda e terceira passagens adicionais vão para a criação de uma conexão TCP. O destinatário confirma o recebimento de cada pacote (ACK) para garantir a entrega confiável.

Se um pacote ou ACK for perdido, o remetente retransmitirá após um tempo limite (RTO, tempo de retransmissão ). O RTO é calculado dinamicamente com base em vários fatores, por exemplo, o atraso esperado do RTT entre o remetente e o destinatário.


Figura 4. A troca de pacotes TCP / TLS inclui um mecanismo de retransmissão.

Para determinar como o TCP funcionava em nossos aplicativos, monitoramos os pacotes TCP com o tcpdump por uma semana no tráfego de combate vindo dos servidores de fronteira da Índia. Em seguida, analisamos as conexões TCP usando tcptrace . Além disso, criamos um aplicativo Android que envia tráfego emulado para um servidor de teste, imitando o tráfego real o máximo possível. Os smartphones com esse aplicativo foram entregues a vários funcionários que coletaram registros por vários dias.

Os resultados de ambas as experiências foram consistentes entre si. Vimos altos atrasos na RTT; os valores da cauda foram quase 6 vezes superiores ao valor médio; valor médio aritmético dos atrasos - mais de 1 segundo. Muitas conexões estavam com perdas, fazendo com que o TCP retransmitisse 3,5% de todos os pacotes. Em áreas com congestionamento, como aeroportos e estações de trem, observamos uma perda de 7%. Tais resultados lançam dúvidas sobre a convicção convencional de que os esquemas avançados de retransmissão usados ​​nas redes celulares reduzem significativamente a perda de transporte. Abaixo estão os resultados do teste do aplicativo "simulador":
Métricas de redeValores
RTT, milissegundos [50%, 75%, 95%, 99%][350, 425, 725, 2300]
Discrepância RTT, segundos~ 1,2 s em média
Perda de pacotes em conexões instáveisEm média ~ 3,5% (7% em áreas com congestionamento)

Quase metade dessas conexões teve pelo menos uma perda de pacotes, a maioria dos quais eram pacotes SYN e SYN-ACK. A maioria das implementações TCP usa um valor RTO de 1 segundo para pacotes SYN, o que aumenta exponencialmente para perdas subsequentes. O tempo de carregamento do aplicativo pode aumentar devido ao fato de o TCP exigir mais tempo para estabelecer conexões.

No caso de pacotes de dados, as RTOs altas reduzem bastante a utilização útil da rede na presença de perdas temporárias nas redes sem fio. Descobrimos que o tempo médio de retransmissão é de cerca de 1 segundo com um atraso de quase 30 segundos. Esses altos atrasos no nível do TCP causaram tempos limite e novas tentativas de HTTPS, o que aumentou ainda mais a latência e a ineficiência da rede.

Enquanto o percentil 75 da RTT medido era de cerca de 425 ms, o percentil 75 para o TCP era de quase 3 segundos. Isso sugere que a perda forçou o TCP a fazer 7 a 10 passagens para transmitir dados com êxito. Isso pode ser devido a um cálculo RTO ineficaz, à incapacidade do TCP de responder rapidamente à perda dos últimos pacotes na janela e à ineficiência do algoritmo de controle de congestionamento, que não distingue entre perdas e perdas sem fio devido ao congestionamento da rede. Abaixo estão os resultados do teste de perda de TCP:
Estatísticas de perda de pacotes TCPValor
Porcentagem de conexões com pelo menos 1 perda de pacote45%
A porcentagem de conexões com perda durante o estabelecimento da conexão30%
A porcentagem de conexões com perda durante a troca de dados76%
Distribuição do retardo de retransmissão, segundos [50%, 75%, 95%, 99%][1, 2.8, 15, 28]
Distribuição de retransmissões para um único pacote ou segmento TCP[1,3,6,7]

Aplicação QUIC


Originalmente projetado pelo Google, o QUIC é um protocolo de transporte moderno e multiencadeado que roda sobre o UDP. No momento, o QUIC está em processo de padronização (já escrevemos que existem, por assim dizer, duas versões do QUIC, os curiosos podem seguir o link - aproximadamente tradutor). Conforme mostrado na Figura 5, o QUIC é hospedado em HTTP / 3 (na verdade, HTTP / 2 em cima de QUIC - este é o HTTP / 3, que agora é altamente padronizado). Ele substitui parcialmente as camadas HTTPS e TCP, usando UDP para formar pacotes. O QUIC suporta apenas transferência segura de dados, uma vez que o TLS está totalmente integrado no QUIC.


Figura 5: O QUIC funciona no HTTP / 3, substituindo o TLS, que costumava funcionar no HTTP / 2.

Abaixo, listamos os motivos que nos convenceram a usar o QUIC para fortalecer o TCP:
  • Configuração da conexão 0-RTT. O QUIC permite a reutilização de autorizações de conexões anteriores, reduzindo o número de handshakes de segurança. No futuro, o TLS1.3 suportará 0-RTT, mas um handshake TCP de três vias ainda será necessário.
  • Superando o bloqueio de HoL. O HTTP / 2 usa uma conexão TCP para cada cliente para melhorar o desempenho, mas isso pode levar a um bloco HoL (cabeçalho da linha). O QUIC simplifica a multiplexação e entrega solicitações ao aplicativo independentemente uma da outra.
  • gerenciamento de congestionamentos. O QUIC está no nível do aplicativo, facilitando a atualização do algoritmo principal de transporte, que controla o envio, com base nos parâmetros da rede (quantidade de perda ou RTT). A maioria das implementações de TCP usa o algoritmo CUBIC , que não é ideal para tráfego sensível a atrasos. Algoritmos recentemente desenvolvidos, como o BBR, modelam com mais precisão a rede e otimizam a latência. O QUIC permite usar o BBR e atualizar esse algoritmo à medida que ele melhora .
  • compensar as perdas. O QUIC chama dois TLPs ( probe de perda de cauda ) antes que o RTO seja acionado - mesmo quando as perdas são muito visíveis. Isso é diferente das implementações de TCP. O TLP retransmite principalmente o último pacote (ou novo, se houver) para acionar o reabastecimento rápido. O processamento do atraso de cauda é especialmente útil para o modo como o Uber trabalha com a rede, principalmente para transmissões de dados curtas, episódicas e sensíveis a atrasos.
  • ACK otimizado. Como cada pacote possui um número de série exclusivo, não há problema em distinguir entre pacotes quando eles são retransmitidos. Os pacotes ACK também contêm tempo para processar o pacote e gerar ACKs do lado do cliente. Esses recursos garantem que o QUIC calcule o RTT com mais precisão. O QUIC ACK suporta até 256 intervalos NACK , ajudando o remetente a ser mais resiliente à troca de pacotes e usar menos bytes no processo. ACK seletivo ( SACK ) no TCP não resolve esse problema em todos os casos.
  • migração de conexão. As conexões QUIC são identificadas por um ID de 64 bits; portanto, se o cliente alterar os endereços IP, você poderá continuar usando o ID da conexão antiga no novo endereço IP, sem interrupção. Essa é uma prática muito comum para aplicativos móveis quando um usuário alterna entre conexões Wi-Fi e celulares.

Alternativas ao QUIC


Analisamos abordagens alternativas para resolver o problema antes de escolher o QUIC.

Primeiro, tentamos implantar TPC PoPs (Pontos de Presença) para concluir as conexões TCP mais próximas dos usuários. Essencialmente, os PoPs encerram a conexão TCP com o dispositivo móvel mais próximo da rede celular e aproximam o tráfego da infraestrutura original. Aproximando o TCP, podemos reduzir o RTT potencialmente e garantir que o TCP seja mais responsivo a ambientes sem fio dinâmicos. No entanto, nossos experimentos mostraram que, na maioria das vezes, o RTT e as perdas são provenientes de redes celulares e o uso de PoPs não fornece melhorias significativas de desempenho.

Também procuramos ajustar os parâmetros TCP. A configuração da pilha TCP em nossos servidores de borda heterogêneos foi difícil porque o TCP possui implementações diferentes em diferentes versões do sistema operacional. Foi difícil implementar isso e testar várias configurações de rede. A configuração do TCP diretamente em dispositivos móveis não foi possível devido à falta de autoridade. Mais importante, chips como conexões com 0-RTT e previsão de RTT aprimorada são críticos para a arquitetura do protocolo e, portanto, não é possível obter benefícios significativos apenas configurando o TCP.

Por fim, avaliamos vários protocolos baseados em UDP que solucionam problemas de streaming de vídeo - queríamos saber se esses protocolos ajudariam em nosso caso. Infelizmente, eles careciam de muitas configurações de segurança e também precisavam de uma conexão TCP adicional para obter metadados e informações de controle.

Nossa pesquisa mostrou que o QUIC é quase o único protocolo que pode ajudar com o problema do tráfego da Internet, levando em consideração a segurança e o desempenho.

Integração QUIC na plataforma


Para integrar com êxito o QUIC e melhorar o desempenho do aplicativo em condições de comunicação ruins, substituímos a pilha antiga (HTTP / 2 sobre TLS / TCP) pelo protocolo QUIC. Usamos a biblioteca de rede Cronet da Chromium Projects , que contém a versão original do protocolo do Google, o gQUIC. Essa implementação também está sendo constantemente aprimorada para seguir a mais recente especificação IETF.

Primeiro, integramos o Cronet em nossos aplicativos Android para adicionar suporte a QUIC. A integração foi realizada para minimizar os custos de migração. Em vez de substituir completamente a pilha de rede antiga que usava a biblioteca OkHttp , integramos o Cronet UNDER à estrutura da API OkHttp. Ao integrar dessa maneira, evitamos alterações em nossas chamadas de rede (que o Retrofit usa) no nível da API.

Semelhante à abordagem dos dispositivos Android, implementamos o Cronet nos aplicativos Uber para iOS, interceptando o tráfego HTTP das APIs da rede usando o NSURLProtocol . Essa abstração, fornecida pela iOS Foundation, processa dados de URL específicos do protocolo e garante que podemos integrar o Cronet em nossos aplicativos iOS sem custos significativos de migração.

Quic concluído no Google Cloud Balancers


No lado de back-end, a terminação QUIC é fornecida pela infraestrutura de balanceamento do Google Cloud Load, que usa cabeçalhos alt-svc nas respostas para oferecer suporte ao QUIC. Em geral, o balanceador adiciona o cabeçalho alt-svc a cada solicitação HTTP e já valida o suporte QUIC para o domínio. Quando o cliente Cronet recebe uma resposta HTTP com esse cabeçalho, ele usa QUIC para solicitações HTTP subseqüentes a este domínio. Assim que o balanceador concluir o QUIC, nossa infraestrutura envia explicitamente essa ação via HTTP2 / TCP para nossos data centers.

Desempenho: Resultados


O excelente desempenho é o principal motivo de nossa busca por um protocolo melhor. Primeiro, criamos um suporte com emulação de rede para descobrir como o QUIC se comportará com diferentes perfis de rede. Para testar a operação do QUIC em redes reais, experimentamos Nova Délhi usando tráfego de rede emulado muito semelhante às chamadas HTTP no aplicativo do passageiro.

Experiência 1


Inventário para experiência:
  • Dispositivos de teste Android com pilhas OkHttp e Cronet para garantir o envio de tráfego HTTPS por TCP e QUIC, respectivamente;
  • um servidor de emulação baseado em Java que envia o mesmo tipo de cabeçalhos HTTPS nas respostas e carrega os dispositivos clientes para receber solicitações deles;
  • Proxies na nuvem localizados fisicamente perto da Índia para concluir as conexões TCP e QUIC. Embora tenhamos usado o proxy reverso no NGINX para concluir o TCP, foi difícil encontrar o proxy reverso de código aberto para o QUIC. Criamos o proxy reverso para o QUIC, usando a pilha QUIC básica do Chromium e o publicamos no cromo como código aberto.


Figura 6. O conjunto de viagens para os testes TCP vs QUIC consistia em dispositivos Android com OkHttp e Cronet, proxies na nuvem para finalizar conexões e um servidor de emulação.

Experiência 2


Quando o Google disponibilizou o QUIC usando o Google Cloud Load Balancing , usamos um mesmo inventário, mas com uma modificação: em vez do NGINX, levamos os balanceadores do Google para concluir as conexões TCP e QUIC dos dispositivos, bem como para direcionar o tráfego HTTPS para o servidor de emulação . Os balanceadores são distribuídos em todo o mundo, mas usam o servidor PoP mais próximo ao dispositivo (graças à geolocalização).


Figura 7. No segundo experimento, queríamos comparar o atraso de conclusão do TCP e o QUIC: usando o Google Cloud e nosso proxy de nuvem.

Como resultado, várias revelações nos aguardavam:
  • A terminação de PoP melhorou o desempenho do TCP. Como os balanceadores concluem a conexão TCP mais próxima dos usuários e são perfeitamente otimizados, isso resulta em menor RTT, o que melhora o desempenho do TCP. QUIC , TCP ( 10-30 ).
  • (hops) . QUIC- ( 50 ), , – 15%- 20%- 99 TCP. , – (bottleneck) .


8. , QUIC TCP.


, QUIC Android iOS-. A/B , QUIC Uber. , , .

(95 99 ) – LTE, 3G, 2G.

9. QUIC TCP .


, – QUIC , , :


, , 80% QUIC , 15% QUIC TCP. , - , Cronet TCP , UDP- . , QUIC.

QUIC


, . . , , TCP QUIC . QUIC- .

, .

Source: https://habr.com/ru/post/pt463073/


All Articles