Banco de Dados

8 mai, 2018

Nginx + Redis: uma opção para melhorar a performance do seu site

Publicidade

Praticamente todo site ligado a varejo tem um medo na Black Friday:

“Será que o site vai aguentar os acessos?”

Quando entrei no Promobit tivemos o mesmo problema. Precisávamos melhorar nossa infraestrutura para aguentar 10x mais acessos que um dia normal. Isso, claro, elevando minimamente o custo.

Naquele momento nossa infraestrutura era Nginx, PHP-FPM, Redis para o Cache e MySql para o banco de dados. Estávamos utilizando o Redis para cachear o conteúdo que vinha do banco de dados, ou que fosse necessário algum tipo de processamento, mas os servidores web não estavam aguentando uma grande quantidade de requisições.

Ao analisar o problema, identificamos que arquivos estáticos aguentavam muito mais requisições, afinal, será que o problema era o PHP?

E se eu te disser que existe uma forma do Nginx acessar o conteúdo que você precisa sem “encostar” no PHP? Hoje, na estrutura atual, mesmo o conteúdo sendo cacheado no Redis, o simples fato do Nginx chamar o PHP está comprometendo a performance do servidor.

Você ainda não sabe o que é o Redis ? Vale dar uma olhada no artigo do nosso amigo:

A ideia aqui não é dizer se essa solução é melhor ou pior do que configurar um Varnish para isso, mas simplesmente mostrar que existe essa opção. Gosto da parte de infraestrutura, mas eu sou um simples desenvolvedor que é curioso, e isso me salvou naquela época.

Vamos em partes. Na prática, o PHP vai renderizar a página normalmente, mas ao finalizar você deve salvar a informação no cache utilizando a URI que você esta acessando como chave.

Algo assim:

<?php
ob_start();

//faz as conexões com banco necessarias
//processa as informações
//printa o resultado na view normalmente
echo 'Resultado na view '. time();

//$redis = conexão com servidor de redis
$redis->set($_SERVER['REQUEST_URI'], ob_get_contents());
ob_flush();

A função ob_start() irá ativar o buffer de saída. Enquanto o buffer de saída estiver ativo, o PHP não renderiza nada. Ao invés disso, a saída é guardada em um buffer interno.

Para obter as informações que estão no buffer interno, basta chamar a função ob_get_contents(). Salvamos esse conteúdo no Redis, e por fim, utilizamos a função ob_flush() para enviar a saída do buffer interno e liberar o buffer.

Para mais informações sobre as funções de manipulação de buffer:

Feito isso, ao acessar essa chave no Redis, você verá o resultado da página como um todo no cache, inclusive o HTML da página.

Se você acessou a página pelo link http://meuprojeto.com.br/index/inicio, a chave que guarda a informação no Redis será a “/index/inicio”.

Agora vamos ao Nginx. Temos que configurar para que antes de chamar o PHP-FPM, ele verifique se o conteúdo que está acessando já não existe no Redis. Para isso vamos precisar que o módulo do Redis para o Nginx esteja instalado:

Após a instalação do módulo falta apenas configurar:

server {
    location / {
        set $redis_key $uri;

        redis_pass     localhost:6379;
        default_type   text/html;
        error_page      401 404 405 = @fallback;
        error_page      502 = @fallback;        
    }

    location @fallback {
        try_files $uri @rewrite;
    }

         location @rewrite {
        rewrite ^/(.*)$  /index.php?q=$1 last;
    }        


    location ~ \.php$ {
                try_files $uri =404;
            fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
        }
}

Note que a variável $redis_key recebe o valor da $uri atual, e aqui no parâmetro redis_pass estamos informando o local do redis server e a porta que esta rodando.

Automaticamente, se ele encontrar a informação no Redis utilizando como chave a URI, ele vai retornar essa informação sem consultar o PHP. Agora, se ela não existir, vai cair em nosso @fallback e retomará todo o processo normal, encaminhando a requisição para o PHP-FPM.

Uma forma simples – mas efetiva – de aumentar a quantidade de requisições que seu servidor pode aguentar, uma vez que a performance do Redis é excelente. No meu caso eu precisei de cerca de metade dos servidores web que eu ia precisar naquele Black Friday em comparação com estrutura antiga, além de ter economizado na minha conta da AWS.

Esse case foi apenas do ano de 2016. No próximo artigo abordarei qual foi a stack que montamos para suportar a Black Friday de 2017, onde aguentamos, no pico, cerca de mil req/s, além de ensinar como configurar um ou mais redis slave para distribuir melhor a leitura desse dado na sua aplicação.