É extremamente interessante observar o protocolo QUIC, por isso adoramos escrever sobre ele. Enquanto as publicações anteriores sobre QUIC tinham um caráter mais histórico (um tanto local, se preferir) e técnico, hoje estamos felizes em publicar uma tradução diferente - trataremos da aplicação prática do protocolo em 2019. E não se trata de uma infraestrutura pequena, baseada em uma garagem qualquer, mas sim da Uber, que opera em quase todo o mundo. Como os engenheiros da empresa chegaram à decisão de usar o QUIC em produção, como realizaram os testes e o que observaram após a implementação em produção - continue lendo.
As imagens são clicáveis. Boa leitura!
A Uber é uma empresa de alcance global, presente em 600 cidades, onde o aplicativo depende completamente da internet sem fio de mais de 4.500 operadoras de celulares. Os usuários esperam que o aplicativo funcione não apenas de forma rápida, mas em tempo real - para garantir isso, o aplicativo da Uber precisa de latências baixas e uma conexão muito confiável. Infelizmente, o stack não se sai bem em redes sem fio dinâmicas e sujeitas a perdas. Constatamos que, nesse caso, a baixa performance está diretamente relacionada às implementações do TCP nos núcleos dos sistemas operacionais.
Para resolver o problema, aplicamos , um protocolo moderno com multiplexação de canais, que nos dá mais controle sobre a performance do protocolo de transporte. Atualmente, o grupo de trabalho está padronizando o QUIC como .
Após testes detalhados, chegamos à conclusão de que a implementação do QUIC em nosso aplicativo reduzirá as latências 'finais' em comparação ao TCP. Observamos uma diminuição entre 10-30% para o tráfego HTTPS nos aplicativos de motoristas e passageiros. Além disso, o QUIC nos proporcionou controle total sobre os pacotes dos usuários.
Neste artigo, compartilhamos nossa experiência na otimização do TCP para aplicativos da Uber utilizando um stack que suporta o QUIC.
A última palavra em tecnologia: TCP
Hoje, o TCP é o protocolo de transporte mais amplamente utilizado para a entrega de tráfego HTTPS na Internet. O TCP garante um fluxo confiável de bytes, lidando assim com a sobrecarga da rede e com a perda no nível do canal. O uso generalizado do TCP para tráfego HTTPS pode ser atribuído à sua onipresença (quase todos os sistemas operacionais incluem TCP), à disponibilidade na maior parte da infraestrutura (como em balanceadores de carga, proxies HTTPS e CDN) e à funcionalidade "pronta para uso", disponível na maioria das plataformas e redes.
A maioria dos usuários utiliza nosso aplicativo em movimento, e as "latências de cauda" do TCP estavam longe de atender às exigências do nosso tráfego HTTPS em tempo real. Simplificando, isso foi uma experiência comum para usuários em todo o mundo – a Figura 1 reflete as latências em grandes cidades:
Figura 1. O valor das "latências de cauda" varia nas principais cidades onde a Uber opera.
Apesar das latências nas redes indiana e brasileira serem maiores do que nas dos EUA e do Reino Unido, as latências de cauda eram significativamente mais altas do que a média. E isso vale também para os EUA e o Reino Unido.
Desempenho do TCP sobre redes sem fio
O TCP foi criado para redes com fio, ou seja, com foco em links bem previsíveis. No entanto, as redes sem fio têm suas peculiaridades e dificuldades. Em primeiro lugar, as redes sem fio são sensíveis à perda devido a interferências e atenuação do sinal. Por exemplo, redes Wi-Fi são sensíveis a micro-ondas, Bluetooth e outras ondas de rádio. Redes móveis sofrem com a perda de sinal ( perda de percurso ) devido à reflexão/absorção do sinal por objetos e construções, assim como ade torres de Isso leva a latências de ida e volta (RTT) mais significativas (4 a 10 vezes) e variadas Para combater as flutuações na largura de banda e nas perdas, redes móveis geralmente utilizam grandes buffers para picos de tráfego. Isso pode resultar em excessiva espera, o que significa maiores latências. Muitas vezes, o TCP interpreta essa espera como uma perda devido ao aumento do tempo limite, levando o TCP a retransmitir e preenchendo assim o buffer. Esse problema é conhecido como ), e isso é um
problema muito sério. (bufferização de rede excessiva, inflacionamento de buffer da internet moderna.
Finalmente, o desempenho da rede celular varia de acordo com o operador, a região e o horário. Na Figura 2, reunimos as latências medianas do tráfego HTTPS em células dentro de um raio de 2 quilômetros. Os dados foram coletados para os dois maiores operadores de telefonia móvel em Nova Délhi, Índia. Como pode ser observado, o desempenho muda de célula para célula. Além disso, o desempenho de um operador difere do desempenho do outro. Isso é influenciado por fatores como os padrões de entrada na rede, levando em consideração o tempo e a localização, a mobilidade dos usuários e a infraestrutura de rede, considerando a densidade das torres e a proporção dos tipos de rede (LTE, 3G, etc.).
Figura 2. Latências em um raio de 2 quilômetros. Nova Délhi, Índia.
Além disso, o desempenho das redes celulares muda ao longo do tempo. Na Figura 3, mostramos a latência mediana ao longo dos dias da semana. Também observamos uma diferença em uma escala menor – ao longo de um único dia e hora.
Figura 3. As latências de cauda podem variar significativamente em diferentes dias, mas para o mesmo operador.
Tudo o que foi mencionado leva ao fato de que o desempenho do TCP é ineficaz em redes sem fio. No entanto, antes de procurar alternativas ao TCP, queríamos desenvolver uma compreensão clara sobre os seguintes pontos:
- o TCP é o principal responsável pelas latências de cauda em nossos aplicativos?
- As redes modernas têm latências de ida e volta (RTT) significativas e variadas?
- Qual é o impacto do RTT e das perdas no desempenho do TCP?
Análise do desempenho do TCP
Para entender como analisamos o desempenho do TCP, vamos relembrar brevemente como o TCP transmite dados do enviado ao receptor. Primeiro, o remetente estabelece uma conexão TCP, realizando um processo de três vias. : o remetente envia um pacote SYN, aguarda um pacote SYN-ACK do receptor e, em seguida, envia um pacote ACK. As passagens adicionais segunda e terceira são dedicadas à criação da conexão TCP. O receptor confirma o recebimento de cada pacote (ACK) para garantir a entrega confiável.
Se um pacote ou ACK for perdido, o remetente retransmite após o timeout (RTO, ). O RTO é calculado dinamicamente, com base em diferentes fatores, como a latência RTT esperada entre o remetente e o receptor.
Figura 4. A troca de pacotes via TCP/TLS inclui mecanismos de retransmissão.
Para entender como o TCP funcionava em nossos aplicativos, monitoramos pacotes TCP usando durante uma semana no tráfego de produção vindo de servidores de borda indianos. Em seguida, analisamos as conexões TCP usando . Além disso, criamos um aplicativo Android que envia tráfego simulado para um servidor de teste, imitando o tráfego real. Smartphones com este aplicativo foram distribuídos para vários funcionários, que coletaram logs durante vários dias.
Os resultados de ambos os experimentos foram consistentes entre si. Observamos altas latências RTT; os valores extremos eram quase 6 vezes maiores que a mediana; a média das latências foi de mais de 1 segundo. Muitas conexões apresentaram perdas, levando o TCP a retransmitir 3,5% de todos os pacotes. Em áreas congestionadas, como aeroportos e estações, observamos perdas de 7%. Esses resultados colocam em dúvida a crença comum de que as reduzem significativamente as perdas no nível de transporte. Abaixo estão os resultados dos testes do aplicativo 'simulador':
Métricas de rede
Valores
RTT, milissegundos [50%, 75%, 95%, 99%]
[350, 425, 725, 2300]
Desvio de RTT, segundos
Em média ~1,2 s
Perda de pacotes em conexões instáveis
Em média ~3,5% (7% em áreas congestionadas)
Quase metade dessas conexões teve pelo menos uma perda de pacotes, principalmente pacotes SYN e SYN-ACK. A maioria das implementações de TCP utiliza um valor de RTO de 1 segundo para pacotes SYN, que aumenta exponencialmente para perdas subsequentes. O tempo de carregamento de aplicativos pode aumentar porque o TCP levará mais tempo para estabelecer conexões.
No caso de pacotes de dados, altos valores de RTO reduzem significativamente a utilidade da rede em situações de perdas temporárias em redes sem fio. Descobrimos que o tempo médio de retransmissão é de cerca de 1 segundo, com latências extremas de quase 30 segundos. Essas altas latências no nível TCP causaram timeouts de HTTPS e requisições repetidas, o que aumentou ainda mais a latência e a ineficiência da rede.
Enquanto o percentil de RTT medido estava em torno de 425 ms, o percentil para TCP foi de quase 3 segundos. Isso sugere que as perdas forçaram o TCP a realizar de 7 a 10 passagens para transmitir os dados com sucesso. Isso pode ser resultado de um cálculo ineficiente do RTO, bem como da incapacidade do TCP de reagir rapidamente à perda na janela e da ineficiência do algoritmo de controle de congestionamento, que não diferencia perdas sem fio de perdas devido à sobrecarga da rede. Abaixo estão os resultados dos testes de perda do TCP:
Estatística de perda de pacotes TCP
O valor
Percentagem de conexões com pelo menos 1 perda de pacote
45%
Percentagem de conexões com perdas durante o estabelecimento da conexão
30%
Percentagem de conexões com perdas durante a troca de dados
76%
Distribuição de atrasos em retransmissões, segundos [50%, 75%, 95%,99%]
[1, 2.8, 15, 28]
Distribuição do número de retransmissões para um pacote ou segmento TCP
[1,3,6,7]
Aplicação do QUIC
Originalmente projetado pela Google, o QUIC é um protocolo de transporte moderno e multicanal que opera sobre o UDP. Até o momento, o QUIC está (já mencionamos que existem como que duas versões do QUIC, os curiosos – nota do tradutor). Como mostrado na Figura 5, o QUIC está posicionado sob o HTTP/3 (na verdade, o HTTP/2 sobre o QUIC é o HTTP/3, que atualmente está sendo padronizado com vigor). Ele substitui parcialmente os níveis de HTTPS e TCP, utilizando UDP para formar pacotes. O QUIC suporta apenas a transmissão de dados segura, já que o TLS está totalmente integrado ao QUIC.

Figura 5: O QUIC opera sob o HTTP/3, substituindo o TLS, que anteriormente operava sob o HTTP/2.
Abaixo apresentamos as razões que nos convenceram a usar o QUIC para reforçar o TCP:
- Conexão de 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 irá suportar 0-RTT, no entanto, o handshake TCP de três vias ainda será obrigatório.
- superação do bloqueio HoL. O HTTP/2 utiliza uma única conexão TCP para cada cliente, a fim de melhorar o desempenho, mas isso pode levar ao bloqueio HoL (head-of-line). O QUIC simplifica o multiplexação e entrega requisições ao aplicativo de forma independente uma da outra.
- controle de congestionamento. QUIC opera na camada de aplicações, permitindo atualizar mais facilmente o algoritmo de transporte principal, que gerencia o envio com base em parâmetros da rede (como a taxa de perda ou RTT). A maioria das implementações do TCP utiliza o algoritmo , que não é otimizado para tráfego sensível à latência. Algoritmos recentemente desenvolvidos, como , modelam a rede com mais precisão e otimizam a latência. O QUIC permite o uso do BBR e atualizar esse algoritmo à medida que ele .
- para compensação de perdas. O QUIC provoca dois TLP () antes que o RTO entre em ação – mesmo quando as perdas são bastante significativas. Isso difere das implementações do TCP. O TLP retransmite principalmente o último pacote (ou um novo, se houver), para iniciar uma rápida compensação. O tratamento de atrasos finais é especialmente útil para a forma como a Uber lida com a rede, especificamente para transferências de dados curtas, episódicas e sensíveis à latência.
- ACK otimizado. Como cada pacote tem um número sequencial único, não há problema de pacotes durante suas retransmissões. Os pacotes ACK também contêm o tempo para o processamento do pacote e a geração do ACK do lado do cliente. Essas características garantem que o QUIC calcule o RTT com mais precisão. O ACK no QUIC suporta até 256 intervalos de , ajudando o remetente a ser mais resistente à reorganização de pacotes e a usar menos bytes no processo. O ACK seletivo () 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, de modo que, se o cliente mudar de endereços IP, o ID da conexão antiga pode continuar a ser usado no novo endereço IP, sem interrupções. Esta é uma prática bastante comum em aplicativos móveis quando o usuário alterna entre conexões Wi-Fi e móveis.
Alternativas ao QUIC
Consideramos abordagens alternativas para resolver o problema antes de escolher o QUIC.
Primeiro, tentamos implementar os PoPs (Pontos de Presença) do TPC para encerrar conexões TCP mais perto dos usuários. Essencialmente, os PoPs encerram a conexão TCP com o dispositivo móvel mais próximo da rede celular e fazem proxy do tráfego até a infraestrutura original. Ao encerrar o TCP mais próximo, podemos potencialmente reduzir o RTT e garantir que o TCP responda de forma mais dinâmica ao ambiente sem fio. No entanto, nossos experimentos mostraram que, na maior parte, o RTT e as perdas vêm das redes celulares, e o uso de PoPs não proporciona uma melhora significativa no desempenho.
Também examinamos o ajuste dos parâmetros TCP. A configuração do stack TCP em nossos servidores de borda heterogêneos foi difícil, pois o TCP possui implementações incompatíveis em diferentes versões do sistema operacional. Implementar e testar várias configurações de rede foi desafiador. Ajustar o TCP diretamente em dispositivos móveis foi impossível devido à falta de permissões. Mais importante ainda, recursos como conexões 0-RTT e aprimoramento da previsão de RTT são críticos para a arquitetura do protocolo e, portanto, não é possível alcançar uma vantagem substancial apenas ajustando o TCP.
Finalmente, avaliamos vários protocolos baseados em UDP que abordam problemas de streaming de vídeo – queríamos saber se esses protocolos ajudariam no nosso caso. Infelizmente, faltavam muitos ajustes de segurança, e eles também exigiam uma conexão TCP adicional para metadados e informações de controle.
Nossas investigações mostraram que o QUIC é quase o único protocolo que pode ajudar a resolver problemas de tráfego da Internet, levando em consideração tanto a segurança quanto o desempenho.
Integração do QUIC na plataforma
Para integrar com sucesso o QUIC e melhorar o desempenho do aplicativo em condições de conexão ruim, substituímos a pilha antiga (HTTP/2 sobre TLS/TCP) pelo protocolo QUIC. Utilizamos a biblioteca de rede do , que contém a versão original do protocolo desenvolvida pelo Google – gQUIC. Esta implementação também está sempre sendo aprimorada para seguir a última especificação da IETF.
Primeiro, integramos o Cronet em nossos aplicativos Android para adicionar suporte ao QUIC. A integração foi feita de tal forma a minimizar os custos de migração. Em vez de substituir completamente a pilha de rede antiga, que usava a biblioteca , integramos o Cronet SOB o framework OkHttp API. Ao realizar a integração dessa forma, evitamos mudanças em nossas chamadas de rede (que usam ) no nível da API.
Semelhante à abordagem em dispositivos Android, implementamos o Cronet em aplicativos da Uber para iOS, interceptando o tráfego HTTP das redes , usando . Esta abstração, fornecida pela iOS Foundation, lida com dados de URL específicos do protocolo e garante que possamos integrar o Cronet em nossos aplicativos iOS sem custos de migração significativos.
Encerramento do QUIC nos balanceadores do Google Cloud
No lado do backend, o encerramento do QUIC é garantido pela infraestrutura de balanceamento de carga do Google Cloud, que utiliza Cabeçalhos nas respostas para suportar QUIC. De maneira geral, para cada solicitação HTTP, o balanceador adiciona o cabeçalho alt-svc e é ele que valida o suporte a QUIC para o domínio. Quando o cliente Cronet recebe uma resposta HTTP com esse cabeçalho, ele utiliza QUIC para as solicitações HTTP subsequentes para esse domínio. Assim que o balanceador finaliza QUIC, nossa infraestrutura envia claramente essa ação por HTTP2/TCP para nossos data centers.
Desempenho: resultados
O desempenho emitido é a principal razão pela qual estamos buscando o melhor protocolo. Para começar, criamos um stand com , para descobrir como o QUIC se comportaria em diferentes perfis de rede. Para testar a operação do QUIC em redes reais, realizamos experimentos enquanto viajávamos por Nova Délhi, utilizando tráfego de rede emulado que se assemelha bastante a chamadas HTTP em aplicações de passageiros.
Experimento 1
Inventário para o experimento:
- dispositivos de teste Android com as pilhas OkHttp e Cronet, para garantir que estamos enviando tráfego HTTPS por TCP e QUIC, respectivamente;
- um servidor de emulação baseado em Java, que envia cabeçalhos HTTPS semelhantes nas respostas e sobrecarrega os dispositivos clientes para receber suas solicitações;
- proxies em nuvem, que estão fisicamente localizados próximos à Índia, para finalizar conexões TCP e QUIC. Enquanto para finalizar TCP usamos um proxy reverso em , foi difícil encontrar um proxy reverso de código aberto para QUIC. Nós construímos um proxy reverso para QUIC nós mesmos, utilizando o stack base do QUIC do Chromium e ele como código aberto no Chromium.
Figura 6. O conjunto de testes para TCP vs QUIC consistia em dispositivos Android com OkHttp e Cronet, proxies em nuvem para finalizar conexões e um servidor de emulação.
Experimento 2
Quando o Google tornou o QUIC acessível com , usamos o mesmo inventário, mas com uma modificação: em vez do NGINX, utilizamos os balanceadores do Google para finalizar conexões TCP e QUIC dos dispositivos, bem como para direcionar o tráfego HTTPS para o servidor de emulação. Os balanceadores estão distribuídos pelo mundo, mas utilizam o servidor PoP mais próximo do dispositivo (obrigado pela geolocalização).
Figura 7. No segundo experimento, queríamos comparar a latência da finalização do TCP e do QUIC: com o Google Cloud e com nosso proxy em nuvem.
No final, tivemos algumas revelações:
- O término através do PoP melhorou o desempenho do TCP. Uma vez que os balanceadores de carga finalizam a conexão TCP mais perto dos usuários e estão excelentes otimizados, isso resulta em menores RTT, o que melhora o desempenho do TCP. E embora isso tenha menos impacto no QUIC, ele ainda superou o TCP em termos de redução da latência de cauda (de 10 a 30 por cento).
- afetam as caudas . Embora nosso proxy QUIC estivesse mais distante dos dispositivos (com uma latência cerca de 50 ms maior) do que os balanceadores da Google, ele apresentou um desempenho similar – uma redução de 15% na latência em comparação com 20% de redução no 99º percentil do TCP. Isso indica que a transição na última milha é um gargalo na operação da rede.
Figura 8. Os resultados de dois experimentos mostram que o QUIC supera significativamente o TCP.
Tráfego ao vivo
Inspirados pelos experimentos, implementamos suporte ao QUIC em nossos aplicativos Android e iOS. Realizamos testes A/B para determinar o impacto do QUIC nas cidades onde a Uber opera. No geral, observamos uma redução significativa na latência de cauda em diversas regiões, operadoras de telecomunicações e tipos de rede.
Os gráficos abaixo mostram as melhorias percentuais nas caudas (95º e 99º percentis) por macrorregiões e diferentes tipos de rede – LTE, 3G, 2G.
Figura 9. Em testes ao vivo, o QUIC superou o TCP em latências.
Só o começo
Provavelmente, isso é apenas o começo – o lançamento do QUIC em produção trouxe oportunidades incríveis de melhorar o desempenho de aplicativos tanto em redes estáveis quanto instáveis, a saber:
Aumento da cobertura
Analisando o desempenho do protocolo em tráfego real, vimos que cerca de 80% das sessões usaram o QUIC com sucesso para todas requisições, enquanto 15% das sessões usaram uma combinação de QUIC e TCP. Suspeitamos que essa combinação ocorreu porque a biblioteca Cronet reverte para o TCP após um tempo limite, já que não pode distinguir entre falhas reais de UDP e condições ruins de rede. Atualmente, estamos buscando uma solução para esse problema, enquanto trabalhamos na implementação subsequente do QUIC.
Otimização do QUIC
O tráfego de aplicativos móveis é sensível a latências, mas não à largura de banda. Além disso, nossos aplicativos são predominantemente usados em redes celulares. Com base em experimentos, as latências finais ainda são altas, mesmo com o uso de proxies para concluir o TCP e o QUIC próximos aos usuários. Estamos em busca ativa de formas de melhorar o gerenciamento de congestionamento e aumentar a eficiência dos algoritmos de recuperação de perdas do QUIC.
Com essas e outras melhorias, planejamos aprimorar a experiência do usuário, independentemente da rede e da região, tornando o transporte de pacotes mais conveniente e sem costura mais acessível em todo o mundo.
Fonte: habr.com
