nginx + fastCGI + Django - obtendo corrupção de dados nas respostas enviadas ao cliente

10

Estou executando o Django atrás do nginx usando o FastCGI. Descobri que em algumas das respostas enviadas ao cliente, ocorre corrupção aleatória de dados no meio das respostas (pode haver algumas centenas de bytes no meio).

Neste ponto, reduzi-o a ser um bug no manipulador FastCGI do nginx ou no manipulador FastCGI do Django (ou seja, provavelmente um bug no flup), pois esse problema nunca ocorre quando eu executo o servidor Django no modo autônomo (ou seja runserver). Isso acontece apenas no modo FastCGI.

Outras tendências interessantes:

  • Tende a acontecer com respostas maiores. Quando um cliente efetua login pela primeira vez, eles recebem um monte de blocos de 1 MB para sincronizá-los com o banco de dados do servidor. Após a primeira sincronização, as respostas são muito menores (geralmente alguns KB de cada vez). A corrupção sempre parece ocorrer nos blocos de 1 MB enviados no início.

  • Isso acontece com mais frequência quando o cliente está conectado ao servidor via LAN (ou seja, conexão de baixa latência e alta largura de banda). Isso me faz pensar que há algum tipo de condição de corrida no nginx ou flup que é exacerbada por um aumento da taxa de dados.

No momento, eu tive que contornar isso colocando um resumo SHA1 extra no cabeçalho de resposta e solicitando ao cliente que rejeitasse respostas onde o cabeçalho não coincidisse com a soma de verificação do corpo, mas essa é uma solução horrível.

Alguém já experimentou algo assim, ou tem alguma dica sobre como identificar se é o flup ou o nginx que está com falha aqui, para que eu possa registrar um bug com a equipe apropriada?

Agradecemos antecipadamente por qualquer ajuda.

Nota: Eu também postou um bug similar em lighttpd + FastCGI + Django um tempo atrás aqui: /programming/3714489/lighttpd-fastcgi-django-truncated-response-sent-to-client-due-to -inesperado ... mesmo que isso não seja a mesma coisa (truncamento x corrupção), está começando a parecer que o culpado mais comum é o flup / Django, em vez do servidor da web.

Edit: Também devo observar qual é o meu ambiente:

  • OSX 10.6.6 em um Mac Mini

  • Python 2.6.1 (Sistema)

  • Django 1.3 (do tarball oficial)

  • flup 1.0.2 (do ovo Python no site flup)

  • nginx + ssl 1.0.0 (de Macports)

EDIT: Em resposta ao comentário de Jerzyk, o caminho do código que monta a resposta se parece (editado para ser sucinto):

# This returns an objc NSData object, which is an array.array 
# when pushed through the PyObjC bridge
ret = handler( request ) 

response = HttpResponse( ret )
response[ "Content-Length" ] = len( ret )
return response

Eu não acho que é possível que o Comprimento do Conteúdo esteja errado com base nisso, e o AFAIK não tem como marcar um objeto Django HttpResponse como explicitamente binário em oposição ao texto. Além disso, como o problema ocorre apenas de forma intermitente, não creio que isso explique o contrário, provavelmente você o veria em todas as solicitações.

EDIT @ionelmc: Você precisa definir o comprimento do conteúdo no Django - o nginx não define isso para você, conforme o exemplo abaixo, uma vez que eu desabilitei a definição explícita do comprimento do conteúdo:

$ curl -i http://localhost/io/ping
HTTP/1.1 200 OK
Server: nginx/1.0.0
Date: Thu, 23 Jun 2011 13:37:14 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive

AKSJDHAKLSJDHKLJAHSD
glenc
fonte
Se os blocos iniciais não mudam frequentemente ou não são específicos do usuário, talvez gravar no disco e servir diretamente via nginx seja a melhor maneira?
sunn0
Infelizmente, os blocos são específicos do usuário e mudam frequentemente, portanto, nenhum cache desse tipo seria apropriado para este aplicativo. Também estou interessado em descobrir o que realmente está causando essa corrupção de dados, em vez de apenas contorná-la (o que já estou fazendo com o resumo extra do SHA1 no cabeçalho).
glenc
Eu posso pensar em duas razões possíveis: codificação incorreta - HttpRespose como texto vs. cabeçalhos binários ou incorretos (especialmente o comprimento do conteúdo) #
1155
1
@glenc o que é um tipo de conteúdo para esta resposta? se isso é binário - você pode tentar configurá-lo? (por exemplo mimetype = 'application / x-ms-excel' ou então)
Jerzyk 04/04
2
Você não precisa definir o tamanho do conteúdo se a sua Codificação de transferência estiver em partes. O rfc 2616 proíbe explicitamente isso: "O campo de cabeçalho Content-Length NÃO DEVE ser enviado se esses dois comprimentos forem diferentes (ou seja, se um campo de cabeçalho Transfer-Encoding estiver presente)."
23611 ionelmc

Respostas:

1

Você tem algum tipo de diretiva nginx cache (bypass / no_cache) ativa para as respostas fastcgi?

No nginx '1.0.3 Changenotes, eles corrigiram uma corrupção de resposta:

Correção de bug: uma resposta em cache pode ser interrompida se os valores das diretivas "proxy / fastcgi / scgi / uwsgi_cache_bypass" e "proxy / fastcgi / scgi / uwsgi_no_cache" forem diferentes; o bug apareceu em 0.8.46.

Fonte: http://nginx.org/en/CHANGES (seção 1.0.3.)

Michel Feldheim
fonte
0

Talvez a corrupção ocasional só ocorra se a saída contiver pelo menos um caractere UTF-8.

O comprimento do conteúdo e o comprimento da sequência não são a mesma coisa, porque um caractere UTF-8 pode conter de 2 a 5 bytes.

Andy Lee Robinson
fonte
Hmmmm .. embora isso seja verdade, não parece provável que seja a causa porque a corrupção estava acontecendo no meio dos blocos de dados e não era simplesmente um caso de falta de dados no final.
glenc
0

Uma maneira de solucionar um pouco mais esse caso seria:

  • tem nginx e django em execução em hardware diferente (para que você possa capturar facilmente o tráfego)
  • capture o tráfego do cliente para - / -> nginx e nginx - / -> django (ou seja, use o wireshark)

Depois de detectar um erro no lado do cliente (com base no sha1), acesse a captura de rede, examine o fluxo gravado (TCP) e tente descobrir se o problema é gerado pelo nginx ou se vem diretamente do django .

cipy
fonte
0

Eu tive um problema muito semelhante que me atormentava desde que eu tinha essa configuração. Como você, eu uso o FastCGI, Nginx e macOS, e encontrei corrupção aleatória no meio de grandes solicitações (eram cerca de 2% das solicitações de um documento de 1,5 MB).

Consegui resolver meu problema alternando para soquetes Unix sobre TCP para a conexão FastCGI entre PHP-FPM (no meu caso) e Nginx. Não sei qual peça do quebra-cabeça é responsável pela corrupção, mas evitar a conexão interna do TCP o corrigiu.

Robert
fonte