OpenResty: transformando o NGINX em um servidor de aplicações completo

OpenResty: transformando o NGINX em um servidor de aplicações completoEstamos publicando novamente a transcrição da palestra da conferência HighLoad++ 2016, que ocorreu em Skolkovo, na região de Moscou, nos dias 7 e 8 de novembro do ano passado. Vladimir Protassov fala sobre como expandir a funcionalidade do NGINX com OpenResty e Lua.

Olá a todos, meu nome é Vladimir Protassov, trabalho na Parallels. Vou contar um pouco sobre mim. Três quartos da minha vida eu escrevo código. Tornei-me programador até o osso — em sentido literal: às vezes sonho com código. Um quarto da vida é desenvolvimento industrial, escrevendo código que vai direto para produção. O código que alguns de vocês usam, mas não têm ideia disso.

Para vocês entenderem o quanto a situação era crítica. Quando eu era um junior, cheguei e me deram bancos de dados de dois terabytes. Agora todos aqui têm alta carga. Eu ia a conferências, perguntava: "Galera, conta pra mim, vocês têm big data, tudo é incrível? Quantos dados vocês têm?" Eles respondiam: "Temos 100 gigabytes!" Eu dizia: "Incrível, 100 gigabytes!" E pensava: como manter uma expressão neutra. Você pensa: sim, eles são legais, mas depois retorna e se depara com esses bancos de dados de múltiplos terabytes. E isso sendo um junior. Consegue imaginar o impacto?

Eu conheço mais de 20 linguagens de programação. Essa é a base que tive que entender no trabalho. Eles lhe dão código em Erlang, C, C++, Lua, Python, Ruby, e você precisa trabalhar com tudo isso. No geral, foi necessário. Não consegui contar exatamente, mas perdi a conta em cerca de 20.

Como todos aqui presentes sabem o que é a Parallels e o que fazemos, não vou falar sobre como somos incríveis ou o que fazemos. Apenas mencionarei que temos 13 escritórios no mundo, mais de 300 funcionários, com desenvolvimento em Moscovo, Tallinn e Malta. Se alguém quiser, pode se mudar para Malta, se estiver frio no inverno e quiser aquecer as costas.

Nosso departamento escreve em Python 2. Estamos focados nos negócios e não temos tempo para implementar as tecnologias da moda, então estamos lidando com isso. Temos Django, porque nele está tudo o que precisamos, e o que é excessivo, jogamos fora. Também MySQL, Redis e NGINX. Além disso, temos várias outras coisas legais. Temos MongoDB, temos coelhinhos correndo, temos de tudo — mas isso não é a minha área e eu não cuido disso.

OpenResty

Falei sobre mim. Vamos entender sobre o que eu vou falar hoje:

  • O que é OpenResty e para que serve?
  • Por que inventar mais uma bicicleta se já temos Python, NodeJS, PHP, Go e outras coisas legais que todos estão satisfeitos?
  • E alguns exemplos da vida real. Eu tive que cortar bastante a apresentação, pois ela estava com 3,5 horas, então haverá poucos exemplos.

OpenResty é NGINX. Graças a ele, temos um servidor web completo, bem escrito e rápido. Eu acho que a maioria de nós usa NGINX em produção. Todos vocês sabem que ele é rápido e excelente. Ele possui um ótimo I/O assíncrono, então não precisamos reinventar a roda como fizemos com gevent em Python. Gevent é incrível, mas se você escrever um código em C e algo der errado, será uma loucura debugar com gevent. Eu tive essa experiência: levei dois dias para descobrir o que havia ocorrido. Se alguém não tivesse investigado isso semanas antes, não tivesse encontrado o problema, não tivesse publicado na internet, e o Google não tivesse achado isso, teríamos enlouquecido.

O NGINX já possui caching e conteúdo estático. Você não precisa se preocupar sobre como fazer isso corretamente, evitando lentidões ou perdendo descritores. O NGINX é muito fácil de implantar, você não precisa se preocupar se deve usar WSGI, PHP-FPM, Gunicorn ou Unicorn. Você instala o NGINX, entrega para os administradores e eles sabem como lidar com isso. O NGINX processa solicitações de forma estruturada. Eu falarei um pouco mais sobre isso depois. Resumidamente, ele tem uma fase em que recebe a solicitação, outra em que a processa, e quando entrega o conteúdo ao usuário.

O NGINX é ótimo, mas há um problema: ele não é flexível o suficiente, mesmo com todos os recursos incríveis que a equipe incorporou na configuração. Essa potência é insuficiente. Por isso, os desenvolvedores do Taobao, há cerca de oito anos, integraram o Lua. O que isso proporciona?

  • Tamanho. Ele é pequeno. LuaJIT oferece cerca de 100-200 kilobytes de overhead em memória e um overhead mínimo em desempenho.
  • Velocidade. O interpretador LuaJIT é, em muitas situações, próximo do C, em algumas ele perde para o Java e em outras o supera. Por um tempo, foi considerado o state of art, o melhor JIT compiler. Atualmente, existem versões melhores, mas são muito pesadas, como o próprio V8. Alguns interpretadores de JS e o HotSpot do Java são mais rápidos em certos pontos, mas ainda perdem em outros.
  • Facilidade de aprendizado. Se você tem uma base de código em Perl e não é a Booking, você não encontrará programadores de Perl. Porque eles não existem, todos foram levados, e ensiná-los é demorado e complicado. Se você precisar de programadores em outra linguagem, talvez tenha que reeducá-los ou encontrá-los. No caso do Lua, é simples. Qualquer um aprende Lua em três dias. Eu levei cerca de duas horas para entender. Depois de duas horas, já estava escrevendo código em produção. Mais ou menos uma semana depois, ele já foi para produção.

O resultado fica assim:

OpenResty: transformando o NGINX em um servidor de aplicações completo

Aqui tem muita coisa. No OpenResty, foram reunidos vários módulos, tanto de Lua quanto da engine. E você tem tudo pronto - implantou e funcionou.

Exemplos

Chega de poesia, vamos ao código. Aqui está um pequeno Hello World:

OpenResty: transformando o NGINX em um servidor de aplicações completo

O que temos aqui? é um location da engine. Nós não nos preocupamos, não escrevemos nosso próprio roteamento, nem pegamos algum pronto - já temos no NGINX, estamos vivendo bem e de maneira preguiçosa.

content_by_lua_block – é um bloco que diz que estamos entregando conteúdo usando um script Lua. Pegamos a variável da engine remote_addr e a inserimos no string.format. É a mesma coisa que sprintf, apenas em Lua, apenas correto. E entregamos ao cliente.

O resultado vai ficar assim:

OpenResty: transformando o NGINX em um servidor de aplicações completo

Mas vamos voltar ao mundo real. Na produção, ninguém implanta Hello World. Normalmente, nossa aplicação acessa um banco de dados ou algo assim e passa a maior parte do tempo aguardando uma resposta.

OpenResty: transformando o NGINX em um servidor de aplicações completo

Ela simplesmente fica esperando. Isso não é muito bom. Quando chegam 100.000 usuários, fica muito difícil para nós. Por isso, vamos criar um exemplo de um aplicativo simples. Vamos buscar imagens, por exemplo, de gatinhos. Mas não vamos buscar simplesmente, vamos expandir as palavras-chave e, se o usuário procurar "gatinhos", vamos encontrar gatinhos, felinos e outros. Para começar, precisamos obter os dados da solicitação no backend. Isso fica assim:

OpenResty: transformando o NGINX em um servidor de aplicações completo

Duas linhas permitem que você recupere os parâmetros GET, sem dificuldades. A seguir, vamos supor que, a partir de um banco de dados com uma tabela por palavra-chave e extensão, recuperamos essa informação através de um comando SQL. É tudo simples. Ele se parece com isto:

OpenResty: transformando o NGINX em um servidor de aplicações completo

Conectamos a biblioteca resty.mysql, que já temos no pacote. Não precisamos instalar nada, está tudo pronto. Indicamos como nos conectar e fazemos a consulta SQL:

OpenResty: transformando o NGINX em um servidor de aplicações completo

Aqui é um pouco assustador, mas tudo funciona. Aqui, 10 é o limite. Estamos recuperando 10 registros, somos preguiçosos, não queremos mostrar mais. Esqueci de mencionar o limite no SQL.

Em seguida, encontramos imagens para todas as consultas. Reunimos um conjunto de consultas e preenchedemos uma tabela Lua chamada reqs, e fazemos ngx.location.capture_multi.

OpenResty: transformando o NGINX em um servidor de aplicações completo

Todas essas consultas são feitas em paralelo, e recebemos as respostas. O tempo de execução é igual ao tempo da resposta mais lenta. Se todos respondem em 50 milissegundos, e enviamos cem consultas, a resposta chegará em 50 milissegundos.

Como somos preguiçosos e não queremos escrever o processamento HTTP e cache, faremos com que o NGINX faça tudo por nós. Como você viu, havia uma consulta para url/fetch, aqui está:

OpenResty: transformando o NGINX em um servidor de aplicações completo

Fazemos um simples proxy_pass, indicamos onde armazenar em cache, como fazer isso, e tudo funciona.

Mas isso não é suficiente, ainda precisamos enviar os dados ao usuário. A ideia mais simples é serializar tudo em JSON, facilmente, em duas linhas. Definimos o Content-Type, entregamos o JSON.

Mas há uma dificuldade: o usuário não quer ler JSON. Precisamos envolver os desenvolvedores front-end. Às vezes, não estamos muito a fim de fazer isso no começo. E os especialistas em SEO dirão que, se estamos procurando imagens, isso não importa para eles. Mas se estamos fornecendo algum conteúdo, eles dirão que os buscadores não indexam nada.

O que fazer com isso? Claro, iremos enviar HTML ao usuário. Gerar manualmente não é adequado, então queremos usar modelos. Para isso, existe a biblioteca lua-resty-template.

OpenResty: transformando o NGINX em um servidor de aplicações completo

Você provavelmente notou as três letras assustadoras OPM. O OpenResty vem com seu próprio gerenciador de pacotes, através do qual podemos instalar um monte de outros módulos, em particular, lua-resty-template. Este é um motor de templates simples, semelhante ao Django templates. É possível escrever código e fazer substituição de variáveis.

Como resultado, tudo ficará com esta aparência:

OpenResty: transformando o NGINX em um servidor de aplicações completo

Compilamos os dados e renderizamos o modelo novamente em duas linhas. O usuário está feliz, recebeu gatinhos. Como ampliamos a consulta, ele também recebeu uma foca. Vai saber, talvez ele estivesse procurando exatamente isso, mas não conseguia formular seu pedido corretamente.

Tudo é ótimo, mas estamos em desenvolvimento e ainda não queremos mostrar aos usuários. Vamos implementar a autenticação. Para isso, vamos ver como o NGINX lida com a requisição em termos de OpenResty:

  • A primeira fase — access, quando o usuário acaba de chegar, e analisamos os cabeçalhos, o endereço IP, e outros dados. Podemos imediatamente bloquear o usuário se não gostarmos dele. Isso pode ser usado para autenticação ou, se recebermos muitas requisições, pode ser fácil bloqueá-las nesta fase.
  • rewrite. Reescrevemos alguns dados da requisição.
  • conteúdo. Entregamos o conteúdo ao usuário.
  • filtro de cabeçalhos. Alteramos os cabeçalhos da resposta. Se usarmos proxy_pass, podemos reescrever alguns cabeçalhos antes de entregá-los ao usuário.
  • filtro de corpo. Podemos alterar o corpo.
  • registro — logging. Podemos escrever logs no elasticsearch sem uma camada adicional.

Nosso sistema de autenticação será aproximadamente assim:

OpenResty: transformando o NGINX em um servidor de aplicações completo

Vamos adicionar isso àquele localização, que descrevemos anteriormente, e colocar o seguinte código lá:

OpenResty: transformando o NGINX em um servidor de aplicações completo

Verificamos se temos o cookie token. Se não houver, redirecionamos para a autenticação. Os usuários são espertos e podem adivinhar que precisam colocar o cookie token. Portanto, também o armazenaremos no Redis:

OpenResty: transformando o NGINX em um servidor de aplicações completo

O código para trabalhar com Redis é muito simples e não difere de outras linguagens. Além disso, toda a entrada/saída, tanto lá quanto aqui, é não bloqueante. Se você escrever código síncrono, ele funcionará de maneira assíncrona. É um pouco como com gevent, mas bem feito.

OpenResty: transformando o NGINX em um servidor de aplicações completo

Vamos implementar a autenticação:

OpenResty: transformando o NGINX em um servidor de aplicações completo

Dizemos que precisamos ler o corpo da requisição. Obtemos os argumentos POST, verificamos se o login e a senha estão corretos. Se estiverem incorretos, redirecionamos para a autenticação. Se estiverem corretos, gravamos o token no Redis:

OpenResty: transformando o NGINX em um servidor de aplicações completo

Não esqueçamos de definir o cookie, isso também é feito em duas linhas:

OpenResty: transformando o NGINX em um servidor de aplicações completo

Este é um exemplo simples e hipotético. Claro, não pretendemos fazer um serviço que mostre gatinhos às pessoas. Mas quem pode nos julgar? Portanto, vamos revisar o que pode ser feito em produção.

  • Um backend minimalista. Às vezes, precisamos fornecer apenas um pouco de dados no backend: em algumas situações, precisamos inserir uma data, em outras, exibir uma lista, informar quantos usuários estão no site, conectar um contador ou estatísticas. Algo pequeno assim. É muito fácil configurar fragmentos mínimos. Isso pode ser feito rapidamente, de forma simples e eficaz.
  • Pré-processamento de dados. Às vezes, queremos integrar anúncios em nossa página, e esses anúncios são obtidos através de solicitações de API. Isso é muito fácil de fazer aqui. Não sobrecarregamos nosso backend, que já trabalha arduamente. Podemos simplesmente montar aqui. Podemos juntar alguns JS ou, ao contrário, separá-los, pré-processando algo antes de entregá-lo ao usuário.
  • Facade para microserviço. Este também é um ótimo caso, que já implementei. Antes, trabalhei na empresa Tenzor, que lida com relatórios eletrônicos e fornece relatórios para cerca da metade das pessoas jurídicas no país. Criamos um serviço onde muitos recursos foram desenvolvidos usando esse mesmo mecanismo: roteamento, autenticação e mais.
    OpenResty pode ser usado como a cola para seus microserviços, proporcionando acesso unificado a tudo e uma interface comum. Como os microserviços podem ser escritos em diferentes linguagens, como Node.js, PHP, Python ou até mesmo Erlang, entendemos que não queremos reescrever o mesmo código em todos os lugares. Por isso, o OpenResty pode ser implementado na frente.
  • Estatísticas e análises. Normalmente, o NGINX está na entrada, e todas as solicitações passam por ele. É aqui que é muito conveniente coletar dados. Podemos calcular algo imediatamente e enviar para algum lugar, por exemplo, Elasticsearch, Logstash ou simplesmente registrar em um log e depois encaminhar.
  • Sistemas multiusuário. Por exemplo, jogos online também são muito bem feitos. Hoje, em Cape Town, Alexander Gladish vai falar sobre como prototipar rapidamente um jogo multiplayer usando o OpenResty.
  • Filtragem de solicitações (WAF). Atualmente, é tendência criar firewalls de aplicações web, e há muitos serviços que os oferecem. Com o OpenResty, você pode criar um firewall de aplicações web que filtra solicitações conforme suas necessidades de forma simples e rápida. Se você está usando Python, sabe que o PHP não será injetado, a menos que você o inicie a partir do console em algum lugar. Você sabe que está usando MySQL e Python. Provavelmente, podem tentar realizar um directory traversal e injetar algo no banco de dados. Portanto, é possível filtrar solicitações suspeitas de maneira rápida e econômica logo na frente.
  • Comunidade. Como o OpenResty é baseado no NGINX, ele tem uma vantagem: é a comunidade NGINX. Ela é muito grande, e boa parte das questões que você pode ter no início já foram resolvidas pela comunidade NGINX.

    Desenvolvedores Lua. Ontem conversei com alguns colegas que participaram do dia de aprendizado do HighLoad++ e fiquei sabendo que apenas o Tarantool foi escrito em Lua. Isso não é verdade, pois muitas coisas foram feitas em Lua. Exemplos: OpenResty, servidor XMPP Prosody, motor de jogo Love2D, Lua é usada em Warcraft e em outros lugares. Existem muitos desenvolvedores Lua, e eles têm uma comunidade grande e receptiva. Todas as minhas dúvidas sobre Lua foram resolvidas em questão de horas. Quando você escreve na lista de discussões, em poucos minutos já tem uma enxurrada de respostas explicando as coisas. Isso é incrível. Infelizmente, nem sempre a comunidade é tão amigável em outros lugares.
    Há um GitHub para o OpenResty, onde você pode abrir um issue se algo não estiver funcionando. Há uma lista de discussão no Google Groups, onde é possível discutir questões gerais, e há uma lista em chinês — caso você não domine o inglês, mas saiba chinês.

Resultados

  • Espero ter conseguido transmitir que o OpenResty é um framework muito conveniente voltado para a web.
  • Ele tem uma baixa barreira de entrada, pois o código é semelhante ao que usamos, e a linguagem é bastante simples e minimalista.
  • Ele fornece I/O assíncrono sem callbacks, não teremos o emaranhado que às vezes podemos criar no NodeJS.
  • Ele tem um deploy fácil, pois só precisamos do NGINX com o módulo necessário e nosso código, e tudo funciona imediatamente.
  • Uma grande e receptiva comunidade.

Não entrei em detalhes sobre como a roteirização é feita, teria sido uma explicação muito longa.

Obrigado pela atenção!

Por que no hosting comum a administração do PrestaShop é lenta, e no seu não é?

Vladimir Protasov - OpenResty: transformando o NGINX em um servidor de aplicações completo

Fonte: habr.com
Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS 🔥 Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS | ProHoster