Proxy reverso Nginx causando downloads de arquivos que demoram muito para serem iniciados

1

Visão geral

Meu aplicativo da Web permite que os usuários carreguem arquivos armazenados em s3 por meio de meus servidores. Quando um usuário solicita um arquivo, meu servidor da Web o recupera de s3 e o envia para o cliente.

Recentemente, implantei um balanceador de carga, configurando minha configuração atual da seguinte maneira:

Nomomento,tenhoapenasumúnicoservidordaWebparasimplificaradepuração.

ProblemaInicial

Depoisdeimplantarobalanceadordecarga,noteiqueosdownloadsdearquivosmaiores(qualquercoisamaiorquecercade4MB)falhariamcomotempolimitedogateway504após60segundos.

Euolheiparaologdeerrosdonginxdobalanceadordecargadositeevialgumasentradascomo:

[error]11770#11770:*40upstreamtimedout(110:Connectiontimedout)whilereadingresponseheaderfromupstream,client:XXXX,...

QuandoeuolheinologdeerrodonginxdoservidordaWebparaosite,vientradassemelhantes:

[error]6632#6632:*41upstreamtimedout(110:Connectiontimedout)whilereadingresponseheaderfromupstream,client:...[error]6632#6632:*85upstreamtimedout(110:Connectiontimedout)whilereadingresponseheaderfromupstream,client:...[error]7163#7163:*41recv()failed(104:Connectionresetbypeer)whilereadingresponseheaderfromupstream,client:...[error]7505#7505:*41recv()failed(104:Connectionresetbypeer)whilereadingresponseheaderfromupstream,client:...[error]7505#7505:*91recv()failed(104:Connectionresetbypeer)whilereadingresponseheaderfromupstream,client:....

Eolhandooslogsdeerrosdophp-fpmnoservidorwebqueeutinha:

WARNING:[poolwww]child3011,script'/home/forge/XXX.com/public/index.php'(request:"GET /index.php") execution timed out (64.950545 sec), terminating
WARNING: [pool www] child 3011 exited on signal 15 (SIGTERM) after 1140.059968 seconds from start
WARNING: [pool www] server reached pm.max_children setting (5), consider raising it
WARNING: [pool www] child 4260, script '/home/forge/XXX.com/public/index.php' (request: "GET /index.php") execution timed out (68.171099 sec), terminating
WARNING: [pool www] child 4260 exited on signal 15 (SIGTERM) after 160.005837 seconds from start
NOTICE: [pool www] child 4271 started

Eu coloquei isso para baixo para não ter meus tempos de execução de php e timeouts de conexão nginx muito baixos, então eu os aumentei fazendo o seguinte:

  • No balanceador de carga:
    • Adicione proxy_read_timeout 600s; ao /etc/nginx/nginx.conf
  • No servidor da web:
    • Na configuração do site nginx, adicionei fastcgi_read_timeout 600; ao bloco de localização .php.
    • Adicionamos max_execution_time = 600 e default_socket_timeout = 600 à configuração do php-fpm.
    • Adicionada request_terminate_timeout = 300 ao /etc/php/7.0/fpm/pool.d/www.conf

Isso corrigiu meu problema inicial até certo ponto, já que agora posso baixar arquivos maiores (testados até 25 MB).

Próximo problema - downloads lentos

Após a configuração acima, posso fazer o download dos arquivos sem tempo limite, mas o tempo de download é muito longo (~ 300 segundos) e o próprio download é lento (menor preocupação).

O fluxo para baixar um arquivo é o seguinte:

  • Cliente clica em um link que atinge meu servidor
  • Meu servidor da Web acessa o banco de dados e obtém informações como o nome do arquivo em hash e o caminho para o servidor de banco de dados.
  • O servidor da web recupera o arquivo da S3.
  • O servidor da Web responde com o arquivo como um download para a solicitação inicial:

Para referência, a função que está sendo executada no servidor da Web para fazer isso é:

public function show($projectID, $documentID, $revisionID, $fileID)
{
    $fileEntry = File::find($fileID);

    $path = $fileEntry->path();
    $file = Storage::get($path);
    $size = Storage::size($path);

    return Response::make($file, 200)
            ->header('Content-Type', $fileEntry->mime)
            ->header('Content-Disposition', 'attachment; filename="' . $fileEntry->original_filename . '"')
            ->header('Content-Length:', $size);
}

Eu entendo que estou lidando com os arquivos em dobro e no futuro mudarei para redirecionamentos de URL s3 assinados, mas há outras partes do aplicativo onde isso não será prático (pegar uma coleção de arquivos, zipar e enviar para o cliente) e, portanto, gostaria de ganhar algum entendimento.

Qual poderia ser a causa desse problema? Não acredito que tenha encontrado esse problema antes de implantar o balanceador de carga.

Se eu baixar o arquivo diretamente do s3, os tempos de download são uma fração do tempo geral quando chego através do meu servidor, portanto, não acredito que o tratamento duplo seja o problema. Poderia ser o buffer ou o tamanho da memória relacionado?

Informações adicionais:

  • Laravel forge usado para provisionar e os servidores.
  • encerramento SSL no balanceador de carga
  • aplicativo da web do Laravel
  • Linode está tudo hospedado no data center de Cingapura
  • A região S3 é Sydney
  • Problemas foram observados em tráfego extremamente baixo (até 1 cliente)
por cubiclewar 22.03.2016 / 03:25

1 resposta

0

Boa edição, as coisas são muito mais claras.

Parece que este é um tempo limite do aplicativo em PHP. Meu melhor palpite é que o PHP está baixando completamente os arquivos grandes para um local temporário, em seguida, retornando-os, em vez de transmiti-los de volta diretamente. Isso explica a latência, embora não tanto pela lentidão. Eu nem sei se o streaming direto é prático direto do S3 de volta à sua pilha - pesquise se necessário (por você). Eu também veria se o PHP5 faz a diferença, eu achei o PHP7 menos confiável em alguns casos de ponta.

Eu acompanharia os horários exatos para quando as solicitações chegam, atingem cada servidor e as respostas são retornadas por cada servidor para que você possa acompanhar totalmente a solicitação. Isso é especialmente verdadeiro na camada do PHP, adicione o registro para quando o servidor de aplicativos receber a solicitação, quando for obtido a partir do S3 e quando começar a enviá-lo de volta ao cliente.

A velocidade de download é um pouco confusa. Eu iria encontrar uma maneira de testar a velocidade entre o seu servidor PHP e S3 - fazer uma onda ou algo assim - poderia ser um problema de largura de banda simples, ou latência diminuindo a largura de banda disponível. Uma solução alternativa pode ser usar o CloudFront, mas provavelmente não, pois isso só acelerará os downloads na segunda vez em que forem solicitados, não o primeiro.

Depois de ter feito tudo isso, se você não tiver trabalhado, poste as informações que descobrir - especialmente os horários exatos em que algumas solicitações atingiram cada camada e os horários em que as respostas serão retornadas.

    
por 23.03.2016 / 05:40