Resposta HTTP 502 gerada por um proxy depois que ele tenta enviar dados para uma conexão parcialmente fechada (pacote de redefinição)

1

Estou recebendo 502s esporádicos retornados por um servidor proxy. Ao inspecionar o fluxo de pacotes, vejo nginx enviando uma solicitação POST para um socket que o servidor de origem já enviou um [FIN, ACK]. Eu quero entender como isso é possível e quaisquer possíveis soluções. É o problema com a origem (envia o FIN, ACK somente após 5 segundos após o envio da resposta) ou no proxy?

Aqui está a captura de tela do PCAP que ilustra o problema:

Meuentendimento:

  • arespostadaorigeméum[PSH,ACK];
  • Oproxyenviaum[ACK]paraosdadosrecebidoscom[P.](owiresharkconfirmaqueopróximo[ACK]éparao[PSH-ACK]recebidoantes);
  • 7segundossepassaram(anoteotimestampbtw/o[FIN,ACK]eonossoPOST([PSH,ACK]))
  • originenviaum[FIN,ACK].Quandooprimeiro[FIN,ACK]éenviado,amáquinadeestadoTCPdeorigemdeveestarnoestadoFIN_WAIT_1.
  • entãoenviamosoutroPOSTcausandoum[RST]emtroca,jáqueaorigemnãoestavaesperandoum[PSH,ACK].

Pergunta:

  • Qualéapossívelexplicaçãoparaestecaso?
  • Porqueoproxy(nginx)estáenviandooutrasolicitaçãosejárecebeuumFINeestárealmentereconhecendoisso!(onúmerodeconfirmaçãonopacotePOST[PSH,ACK]énaverdadeSEQ_NUMBER+1do[FIN,ACK]-porissoestáreconhecendooFINdebitfantasma.
  • Quaissãoaspossíveisrazõesparaaorigemretornarum[FIN,ACK]somenteapós5segundosenãoimediatamente?Read-timeout/idle-timeout?

Eunãopossuoaorigem-entãonãoconsigocapturarlá.

Detalhesadicionais:

Ologdeerrosnoproxy(logdeerrosdonginx):

2017/04/1706:51:07[error]123091#0:*225010841upstreamprematurelyclosedconnectionwhilereadingresponseheaderfromupstream,client:X.90.10,server:www.example.com,request:"POST /web/?a=b HTTP/1.1", upstream: "http://X.32.238:80/web/?a=b", host: "www.example.com"

Os números SEQ e ACK são exibidos para as últimas solicitações nesta captura de tela:

    
por Mindaugas Bernatavičius 19.04.2017 / 00:11

2 respostas

4

What is the possible explanation for this case?

Uma condição de corrida entre um contador inativo de ~ 5 segundos na origem e uma atividade variável no lado do cliente. A terceira variável envolvida é, obviamente, a latência da rede.

Parece haver um temporizador ocioso de ~ 5 segundos na origem, enquanto seu cliente leva ~ 5 segundos para fazer uma segunda solicitação (POST) através do proxy Nginx. Se o primeiro for maior que o último (incluindo latência de rede), você não terá problemas. Se demorar um pouquinho mais para o pedido do cliente ser enviado, você tem um problema.

Você pode ver como tanto o POST quanto o FIN, ACK do Nginx são enviados praticamente juntos: 2.4ms e 2.6ms após FIN e ACK da origem, respectivamente. Isso poderia te colocar fora do rumo aqui, porque eu não acho que o POST seja uma resposta ao FIN, ACK da origem. Como é enviado 2,4ms após FIN da origem, ACK

Why is the proxy (nginx) sending another request if it already received a FIN and is actually acknowledging it! (the ack number in the POST [PSH, ACK] packet is actually SEQ_NUMBER + 1 of the [FIN,ACK] - so it's acknowledging the phantom bit FIN.

O número ACK no pacote POST é provavelmente o pacote "200 OK". Não há dados extras vindo do lado do servidor após essa resposta HTTP, portanto, qualquer ACK do cliente será ACKING o mesmo número.

Atualização: Agora sabemos que o pacote POST tem o número ACK aumentado em 1, então Nginx sabia sobre [FIN, ACK]. Investigações posteriores mostram que está tudo bem: uma máquina pode enviar uma solicitação e terminar com um [FIN, ACK] se não estiver planejando continuar a conexão depois de receber a resposta do lado remoto, que deveria enviar os dados solicitados de volta e terminar. com a continuação do processo [FIN, ACK].

Isso não muda o fato de que há uma condição de corrida em que a origem decidiu fechar a conexão após 5 segundos ociosa, ignorando assim o pacote POST que vem logo após (e até mesmo enviando um RST de volta - embora não esteja claro se este RST teria sido enviado independentemente).

What are the possible reasons for the origin returning a [FIN,ACK] only after 5 seconds and not immediatelly? Read-timeout / idle-timeout?

Você não precisa retornar um FIN, ACK imediatamente, especialmente desde o HTTP 1.1 e a introdução de conexões persistentes. Estes ~ 5 segundos parecem ser um temporizador ocioso na origem.

As duas coisas são confirmadas aqui: link - incluindo um tempo limite ocioso padrão de 5 segundos no Apache 2.2 ou mais recente.

Soluções sugeridas

Eu não posso realmente sugerir uma solução sem saber mais sobre sua infraestrutura, mas em termos gerais você tem algumas opções:

  • Investigue por que o cliente leva 5 segundos para enviar uma segunda solicitação. Desvantagens: demorado e, provavelmente, implica mudanças na aplicação.
  • Aumenta o tempo limite da origem (Apache?) para talvez 10 segundos. Inconvenientes: Problemas para dimensionar à medida que você mantém mais recursos ociosos. Pode precisar de alterações na aplicação para descartar as conexões o mais rápido possível.
  • Não reutilize a conexão TCP para uma segunda solicitação HTTP, emitindo o cabeçalho "Conexão: Fechar". Desvantagens: Maior custo por solicitação, pois você deve estabelecer uma nova sessão TCP. Pode implicar alterações no aplicativo para emitir o cabeçalho em todas as solicitações ou alterações no Nginx, desviando-se da configuração padrão (custos administrativos aumentados).
  • Use a opção "keepalive" no Nginx dentro de sua configuração upstream para definir um keepalive menor que 5 segundos. Inconveniente: muito tráfego / ruído extra.

Espero que isso ajude:)

    
por 19.04.2017 / 01:08
0

Acho que isso causado pelo soquete do servidor upstream mantém o tempo limite ativo e o soquete será fechado, o socket.setsolinger padrão não será aberto.

Acho que podemos deixar os servidores nginx upstream keepalive timeout。Here outro autor resolvê-lo, veja isto

    
por 13.03.2018 / 10:52