Executando verificação de segurança...
1

Mecânicas de Controle que Mantêm a Internet Fluindo

Olá pessoal, depois de estudar bastante sobre redes decidi trazer uma perspectiva para desenvolvedores web sobre como sabemos tão pouco desse contexto, hoje em dia nos acostumamos a pensar que todo hardware é capaz, nunca nos faltará recurso e o tempo de resposta será bom o suficiente, porém aqui quando saimos da camada de aplicação isso passa a não ser realidade, roteadores domésticos tem capacidade média de 64 KB a 256 KB em seu buffer que recebe dados, você logo deve pensar, hoje em dia temos acesso bem mais barato a hardware, por que não aumentam essa capacidade?

Esse artigo foi publicado originalmente no meu blog e lá contém imagens explicativas https://suhett.com/mecanicas-de-controle-que-mantem-a-internet-fluindo

Vamos imaginar o seguinte cenário,

Um Sender está enviando dados a uma taxa de 4 segments por segundo.
Esses segments passam por um BUFFER antes de chegar ao Receiver. As características do buffer são:

  • Capacidade: 1 MB (Megabyte), o que equivale a 1.048.576 bytes.
  • Tamanho do segmento: Cada segmento tem 1500 bytes (um valor aproximado).

Nosso Sender é quem está enviando dados pela rede, temos também os segments que são os dados propriamente ditos, e esses segments são enviados para um Receiver, que contém o buffer para retenção de segments, onde os pacotes que estão esperando o ack se acumulam.

  • Bytes por segundo: O sender envia 4 segmentos por segundo, e cada segmento tem 1500 bytes. Portanto, a taxa de envio é 4×1500=6000 bytes/s.

  • Tempo para encher o buffer: O buffer tem uma capacidade de 1.048.576 bytes. Dividindo a capacidade pela taxa de envio, chegamos a aproximadamente 174,76 segundos. Isso significa que levará cerca de 174,76 segundos para encher o buffer completamente.

Com um buffer de 1 MB nosso Receiver permitiria que o Sender enviasse durante quase 3 minutos dados que nunca seriam recebidos, causando bufferbloat que é o aumento excessivo da latência e jitter na rede, causado principalmente por buffers grandes demais, onde ao invés de descartar os pacotes quando há congestionamento os armazena, o que atrasa a detecção e controle do congestionamento.

É por isso que não é tão simples aumentar o tamanho do buffer, agora se voltarmos esse buffer para 64 KB, fazendo a mesma conta que fizemos a cima, o resultado do tempo é de apenas 10.92 segundos e você pode achar que isso resolve o problema, mas não resolve, nós precisamos de mais, nós precisamos de um algoritmo robusto para gerenciar essa fila que pode congestionar a rede e seus dispositivos.

Congestion Control

Como você pode ver roteadores tem limite, a grande questão que queremos responder aqui é qual o limite dos nossos roteadores? Se você ainda não reparou, enviar o máximo possível de dados pelos nossos roteadores é o nosso trabalho, não só em quantidade mas em velocidade e confiabilidade, para isso usamos estado chamado Congestion Window (CWND).

Congestion Window é um dado TCP que limita quanto de dados uma conexão TCP pode enviar.

Cada algoritmo lida com a CWND de uma forma diferente, mas o conceito básico que você precisa entender antes de ver cada implementação é, quando o Sender consegue enviar um segment e receber um ack ele aumenta o tamanho da CWND permitindo que mais dados passem por ele expandindo seu volume, quando Sender para de receber acks ele diminui o tamanho da CWND, assim o Sender sabe que a rede não vai bem.

Para facilitar as diferentes implementações vamos utilizar de referência esse paper https://www.rfc-editor.org/rfc/pdfrfc/rfc2581.txt.pdf

Slow Start

Esse algoritmo começa com o CWND baixo utilizando o número 1, por isso o nome de “inicio lento” apesar disso se engana se você pensa que ele não é veloz, esse algoritmo por definição escala exponencialmente onde para cada ack você soma uma unidade de MSS à CWND, e esse ciclo se repete até que a CWND atinja seu threshold.

💡MSS significa Maximum Segment Size que é uma “dependência” do MTU (Maximum Transmission Unit) configurado na rede. Normalmente o MTU é 1500 e resulta em um MSS de 1460.

No Início, o algoritmo é configurado da seguinte forma:

  • A cwnd (janela de congestionamento) é inicializada com 1 MSS. Isso significa que, no começo, apenas um segmento pode ser enviado por vez.
  • A ssthresh (limite de início lento) é definida como um número muito alto. Isso garante que a fase de "início lento" se prolongue até que outro evento a altere.

Ao Receber um ACK

Quando um ACK é recebido, o algoritmo verifica o estado da cwnd em relação à ssthresh:

  • Se cwnd for menor que ssthresh: O algoritmo está na fase de "início lento" (Slow Start). A cwnd é aumentada exponencialmente em 1 MSS para cada ACK recebido.
  • Caso contrário (se cwnd for maior ou igual a ssthresh): O algoritmo entra na fase de "evitar congestionamento" (Congestion Avoidance). Nesta fase, o crescimento da cwnd se torna linear, em vez de exponencial, para evitar sobrecarregar a rede.

Congestion Avoidance

Quando o ritmo exponencial chega no seu limite ele entra em um modo que chama-se congestion avoidance, aqui o ritmo muda e passa a ser linear isso porque a CWND não pode passar a RWND que é a janela máxima que o Receiver da conexão consegue lidar.

Entendendo nosso Receiver, voltamos ao funcionamento de rede do congestion avoidance, nós só iremos somar algo ao CWND(que já está com um valor muito alto) quando todos os pacotes tiverem leitura confirmada pelo Receiver e fizerem a viagem completa RTT (Round Trip Time), garantindo que ele está conseguindo lidar com o buffer que está gerenciando.

\text{CWND} = \text{CWND} + \frac{\text{MSS} ^ 2}{\text{CWND}}

Explicit Congestion Notification

Sabendo que não podemos permitir que essa escalada seja ilimitada, o roteador vai chegar no seu limite, e a ECN é utilizada justamente para fazer essa comunicação do roteador com o Sender, pedindo para o Sender parar de enviar pacotes porque a congestão é eminente.

Mas e se o Sender não parar?

Todos os pacotes são descartados quando esse limite for realmente esgotado

  • CWND volta para 1
  • o threshold do Slow Start é cortado na metade
  • Isso pode não parecer muito mas significa uma perda absurda de performance na sua rede

Flow Control

Nós já sabemos como não congestionar a rede, entretanto ainda temos que lidar com a sobrecarga direto no Receiver, isso gera a pergunta

Quantos segments eu posso mandar sem sobrecarregar o Receiver?

Imagine o exemplo que um Sender envia um segment e para cada segment ele espera o Receiver fazer o ack, se isso acontece com sua aplicação backend você estaria perdido pois o tempo total de comunicação seria muito lento, por isso você precisa de Flow Control.

Esse controle existe para sincronizar cliente e servidor em um canal de comunicação onde eles possam extrair a maior velocidade um do outro, sem afetar o funcionamento do Receiver.
Para solucionar isso precisamos introduzir um conceito de window size, que tem por utilidade dizer ao Sender quanto cabe no Buffer do Receiver e como está operando, a window size do Receiver se chama Receive Window (RWND) e fica contida dentro do header TCP.

A cada segment com ack marcado pelo Receiver essa RWND é atualizada, e enquanto isso o Sender pode olhar a cada iteração para continuar enviando segments constantemente enquanto houver disponibilidade.

Já do lado do Sender temos o conceito de Sliding Window onde para qualquer ack o ponteiro na janela avança e se mantém sincronizado com a RWND, isso acontece para o Sender ter a confiança de seguir enviando constantemente os segmentos e saber quais não reenviar no próximo envio

Somando o buffer do Receiver ao conceito de Sliding Window no Sender nós poderemos responder a pergunta que é quanto mandar sem sobrecarregar o nosso receiver.

Conclusão

Eu gostaria muito de abordar todos os tópicos desse assunto mas não terminaria esse post em um mês, se você chegou até aqui saiba que isso é um recorte de muitos anos atrás, diferentes abordagens surgiram para o TCP

Enfim muitas possibilidades e muitos casos reais de engenharia que a gente acaba pensando que “nunca vai usar para nada” não deixe se enganar, lidamos com isso o tempo todo.

Carregando publicação patrocinada...