Estamos publicando novamente a transcrição do relatório da conferência 2016, que aconteceu em Skolkovo, perto de Moscou, de 7 a 8 de novembro do ano passado. explica como estender a funcionalidade NGINX com OpenResty e Lua.
Olá a todos, meu nome é Vladimir Protasov, trabalho na Parallels. Vou contar um pouco sobre mim. Passo três quartos da minha vida escrevendo códigos. Tornei-me um programador completo no sentido literal: às vezes vejo código em meus sonhos. Um quarto da vida é desenvolvimento industrial, escrita de código que vai direto para produção. Um código que alguns de vocês usam, mas não percebem.
Então você entende o quão ruim foi. Quando eu era um pouco júnior, cheguei e recebi esses bancos de dados de dois terabytes. A carga é alta para todos aqui agora. Fui a conferências e perguntei: “Gente, me digam, vocês têm big data, está tudo bem? Quantas bases você tem aí? Eles me responderam: “Temos 100 gigabytes!” Eu disse: “Legal, 100 gigabytes!” E eu estava pensando comigo mesmo como manter cuidadosamente minha expressão impassível. Você pensa, sim, os caras são legais, e então volta e mexe nesses bancos de dados de vários terabytes. E isso é ser júnior. Você pode imaginar que golpe é esse?
Conheço mais de 20 linguagens de programação. Isso é algo que tive que descobrir enquanto trabalhava. Eles fornecem código em Erlang, C, C++, Lua, Python, Ruby, qualquer outra coisa, e você tem que cortar tudo. Em geral, eu tive que fazer isso. Não foi possível calcular o número exato, mas por volta do dia 20 o número foi perdido.
Como todos os presentes sabem o que é o Parallels e o que fazemos, não vou falar sobre o quão legais somos e o que fazemos. Direi apenas que temos 13 escritórios em todo o mundo, mais de 300 funcionários, desenvolvimento em Moscou, Tallinn e Malta. Se desejar, você pode levá-lo e se mudar para Malta se estiver frio no inverno e precisar aquecer as costas.
Especificamente, nosso departamento escreve em Python 2. Estamos no mercado e não temos tempo para implementar tecnologias da moda, por isso sofremos. Usamos Django porque tem tudo, pegamos o que era desnecessário e jogamos fora. Também MySQL, Redis e NGINX. Também temos muitas outras coisas legais. Temos o MongoDB, temos coelhos correndo por aí, temos tudo - mas não é meu e eu não faço isso.
OpenResty
Eu contei sobre mim. Vamos descobrir sobre o que vou falar hoje:
- O que é OpenResty e com o que se come?
- Por que reinventar outra roda quando temos Python, NodeJS, PHP, Go e outras coisas legais que agradam a todos?
- E alguns exemplos da vida. Tive que cortar muito o relatório porque demorei 3,5 horas, então serão poucos exemplos.
OpenResty é NGINX. Graças a ele, temos um servidor web completo, bem escrito e que funciona rapidamente. Acho que a maioria de nós usa o NGINX na produção. Todos vocês sabem que ele é rápido e legal. Eles criaram E/S síncronas legais nele, então não precisamos alternar nada, assim como fizeram com gevent em Python. Gevent é legal, ótimo, mas se você escrever código C e algo der errado, com Gevent você ficará louco ao depurá-lo. Tive a experiência: levei dois dias inteiros para descobrir o que havia de errado ali. Se alguém não tivesse vasculhado por várias semanas, encontrado o problema, escrito na Internet e o Google não o tivesse encontrado, teríamos enlouquecido completamente.
O NGINX já possui cache e conteúdo estático. Você não precisa se preocupar em como fazer isso humanamente, para não desacelerar em algum lugar, para não perder descritores em algum lugar. O Nginx é muito conveniente de implantar, você não precisa pensar no que levar - WSGI, PHP-FPM, Gunicorn, Unicorn. O Nginx foi instalado, entregue aos administradores, eles sabem como trabalhar com ele. O Nginx processa solicitações de maneira estruturada. Falarei sobre isso um pouco mais tarde. Resumindo, tem uma fase em que apenas aceita o pedido, quando o processa e quando entrega o conteúdo ao usuário.
Nginx é legal, mas há um problema: não é flexível o suficiente, mesmo com todos os recursos interessantes que os caras colocaram na configuração, apesar do que pode ser configurado. Este poder não é suficiente. É por isso que os caras do Taobao, há muito tempo, parece que foram oito anos atrás, incorporaram Lua nele. O que isso dá?
- Tamanho. É pequeno. LuaJIT fornece cerca de 100-200 kilobytes de sobrecarga de memória e sobrecarga mínima de desempenho.
- velocidade. O interpretador LuaJIT se aproxima do C em muitas situações, em algumas situações perde para o Java, em outras o supera. Por algum tempo foi considerado o estado da arte, o compilador JIT mais bacana. Agora existem outros mais legais, mas são muito pesados, por exemplo, o mesmo V8. Alguns intérpretes JS e Java HotSpot são mais rápidos em alguns pontos, mas em alguns lugares ainda perdem.
- Fácil de aprender. Se você tiver, digamos, uma base de código Perl e não for Booking, não encontrará programadores Perl. Por não existirem, foram todos levados embora e ensiná-los é longo e difícil. Se você quiser programadores para outra coisa, talvez seja necessário treiná-los novamente ou encontrá-los. No caso de Lua, tudo é simples. Qualquer júnior pode aprender Lua em três dias. Levei cerca de duas horas para descobrir. Duas horas depois eu já estava escrevendo código em produção. Cerca de uma semana depois ele foi direto para a produção e saiu.
Como resultado, fica assim:

Há muito aqui. OpenResty coletou vários módulos, tanto luash quanto de motor. E você tem tudo pronto - implantado e funcionando.
Примеры
Chega de letras, vamos passar para o código. Aqui está um pequeno Olá Mundo:

O que há? Este é um local da Engins. Não nos preocupamos, não escrevemos nosso próprio roteamento, não pegamos um já pronto - já o temos no NGINX, vivemos uma vida boa e preguiçosa.
content_by_lua_block é um bloco que diz que estamos servindo conteúdo usando um script Lua. Pegamos a variável Engins remote_addr e coloque-o string.format. Este é o mesmo que sprintf, apenas em Lua, apenas correto. E nós entregamos ao cliente.
Como resultado, ficará assim:

Mas vamos voltar ao mundo real. Ninguém implanta o Hello World em produção. Nossa aplicação geralmente vai para o banco de dados ou outro lugar e na maioria das vezes espera por uma resposta.

Ele apenas senta e espera. Não é muito bom. Quando chegam 100.000 usuários, é muito difícil para nós. Então, vamos usar um aplicativo simples como exemplo. Procuraremos fotos, por exemplo, de gatos. Mas não vamos apenas pesquisar, vamos ampliar as palavras-chave e, se o usuário pesquisar por “gatinhos”, encontraremos gatos, gatos peludos e assim por diante. Primeiro, precisamos obter os dados da solicitação no backend. Se parece com isso:

Duas linhas permitem que você escolha parâmetros GET, sem complicações. A seguir, digamos, a partir de um banco de dados com um sinal para uma palavra-chave e extensão, obtemos essas informações usando uma consulta SQL regular. É simples. Se parece com isso:

Conectando a biblioteca resty.mysql, que já temos no kit. Não precisamos instalar nada, está tudo pronto. Indicamos como conectar e fazer uma consulta SQL:

É um pouco assustador aqui, mas tudo funciona. Aqui 10 é o limite. Retiramos 10 entradas, somos preguiçosos, não queremos mostrar mais. Esqueci do limite no SQL.
A seguir encontramos fotos para todas as dúvidas. Coletamos um monte de solicitações e preenchemos uma tabela Lua chamada reqs, e nós fazemos ngx.location.capture_multi.

Todas essas solicitações são enviadas em paralelo e as respostas nos são devolvidas. O tempo de operação é igual ao tempo de resposta do mais lento. Se todos atirarmos em 50 milissegundos e enviarmos cem solicitações, receberemos uma resposta em 50 milissegundos.
Como somos preguiçosos e não queremos escrever HTTP e manipulação de cache, faremos com que o NGINX faça tudo por nós. Como você viu, houve um pedido de url/fetchaqui está:

Nós simplificamos proxy_pass, indicamos onde armazenar em cache, como fazê-lo e tudo funciona para nós.
Mas isso não basta, ainda precisamos fornecer os dados ao usuário. A ideia mais simples é serializar tudo em JSON, facilmente, em duas linhas. Damos Content-Type, damos JSON.
Mas há uma dificuldade: o usuário não quer ler JSON. Precisamos atrair desenvolvedores front-end. Às vezes não queremos fazer isso no início. E os especialistas em SEO dirão que se estamos procurando fotos, isso não importa para eles. E se lhes dermos algum conteúdo, eles dirão que nossos motores de busca não indexam nada.
O que fazer sobre isso? Claro, daremos HTML ao usuário. Gerar manualmente não é comum, então queremos usar modelos. Existe uma biblioteca para isso lua-resty-template.

Você provavelmente já viu as três letras assustadoras OPM. OpenResty vem com seu próprio gerenciador de pacotes, através do qual você pode instalar vários módulos diferentes, em particular, lua-resty-template. Este é um mecanismo de template simples, semelhante aos templates do Django. Lá você pode escrever código e realizar substituição de variáveis.
Como resultado, tudo ficará mais ou menos assim:

Pegamos os dados e renderizamos o modelo, novamente em duas linhas. O usuário está feliz, recebeu gatos. Como ampliamos o pedido, ele também recebeu uma foca para gatinhos. Nunca se sabe, talvez ele estivesse procurando exatamente isso, mas não conseguiu formular seu pedido corretamente.
Está tudo legal, mas estamos em desenvolvimento e não queremos mostrar aos usuários ainda. Vamos fazer a autorização. Para fazer isso, vejamos como o NGINX lida com a solicitação nos termos do OpenResty:
- Primeira fase - Acesso, quando o usuário acabou de chegar e olhamos para ele por cabeçalhos, por endereço IP e por outros dados. Podemos cortá-lo imediatamente se não gostarmos. Isto pode ser usado para autorização, ou se recebermos muitos pedidos, podemos facilmente interrompê-los nesta fase.
- reescrever. Reescrevemos alguns dados da solicitação.
- o conteúdo. Entregamos o conteúdo ao usuário.
- filtro de cabeçalhos. Substituímos os cabeçalhos de resposta. Se usássemos
proxy_pass, podemos reescrever alguns cabeçalhos antes de fornecê-los ao usuário. - filtro corporal. Podemos mudar o corpo.
- log - exploração madeireira. Você pode gravar logs no elasticsearch sem uma camada adicional.
Nossa autorização será mais ou menos assim:

Vamos adicionar isso a aquele location, que descrevemos antes, e coloque o seguinte código lá:

Procuramos ver se temos um token de cookie. Caso contrário, pedimos autorização. Os usuários são astutos e podem adivinhar que precisam definir um token de cookie. Portanto, também colocaremos no Redis:

O código para trabalhar com Redis é muito simples e não difere de outras linguagens. Ao mesmo tempo, todas as entradas/saídas, aqui e ali, não estão bloqueando. Se você escrever código síncrono, ele funcionará de forma assíncrona. Quase como gevent, mas bem feito.

Vamos fazer a autorização propriamente dita:

Dizemos que precisamos ler o corpo da solicitação. Recebemos argumentos POST e verificamos se o login e a senha estão corretos. Se estiverem incorretos, desafiaremos você para autorização. E se estiver correto, escreva o token no Redis:

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

O exemplo é simples e especulativo. Claro, não faremos um serviço que mostre gatos às pessoas. Mas quem nos conhece. Então, vamos ver o que pode ser feito na produção.
- Back-end minimalista. Às vezes precisamos enviar apenas alguns dados para o backend: em algum lugar precisamos inserir uma data, em algum lugar precisamos exibir uma lista, dizer quantos usuários estão no site agora, anexar um contador ou estatísticas. Algo tão pequeno. Algumas peças mínimas podem ser feitas com muita facilidade. Isso tornará tudo rápido, fácil e ótimo.
- Pré-processamento de dados. Às vezes, queremos incorporar publicidade em nossa página e recebemos essa publicidade por meio de solicitações de API. Isso é muito fácil de fazer aqui. Não carregamos nosso backend, que já está parado e trabalhando duro. Você pode pegá-lo e coletá-lo aqui. Podemos juntar algum JS ou, inversamente, desacoplá-lo e pré-processar algo antes de entregá-lo ao usuário.
- Fachada para microsserviço. Este também é um caso muito bom, eu o implementei. Antes disso, trabalhei na Tenzor, empresa que lida com relatórios eletrônicos e fornece relatórios para aproximadamente metade das pessoas jurídicas do país. Criamos um serviço, muitas coisas foram feitas lá usando o mesmo mecanismo: roteamento, autorização e muito mais.
OpenResty pode ser usado como cola para seus microsserviços, fornecendo acesso único a tudo e uma interface única. Como os microsserviços podem ser escritos de forma que você tenha Node.js aqui, PHP aqui, Python aqui, alguma coisa de Erlang aqui, entendemos que não queremos reescrever o mesmo código em todos os lugares. Portanto, o OpenResty pode ser conectado na frente. - Estatísticas e análises. Normalmente o NGINX fica na entrada e todas as solicitações passam por ele. É neste local que é muito cómodo recolher. Você pode calcular algo imediatamente e carregá-lo em algum lugar, por exemplo, Elasticsearch, Logstash, ou simplesmente gravá-lo no log e enviá-lo para algum lugar.
- Sistemas multiusuário. Por exemplo, jogos online também são muito bons de se fazer. Hoje na Cidade do Cabo, Alexander Gladysh falará sobre como criar rapidamente um protótipo de um jogo multijogador usando OpenResty.
- Filtragem de Solicitação (WAF). Hoje em dia está na moda fazer todos os tipos de firewalls para aplicações web; existem muitos serviços que os fornecem. Usando o OpenResty, você pode criar um firewall de aplicativo da web que filtrará de maneira simples e fácil as solicitações de acordo com suas necessidades. Se você possui Python, entende que o PHP definitivamente não será injetado em você, a menos, é claro, que você o gere em qualquer lugar do console. Você sabe que tem MySQL e Python. Provavelmente, eles podem tentar fazer algum tipo de passagem de diretório e injetar algo no banco de dados. Portanto, você pode filtrar consultas estranhas de forma rápida e barata logo no início.
- Comunidade. Como o OpenResty é baseado no NGINX, ele tem um bônus - este Comunidade NGINX. É muito grande e uma boa parte das dúvidas que você terá a princípio já foram resolvidas pela comunidade NGINX.
Desenvolvedores Lua. Ontem conversei com a galera que compareceu ao dia de treinamento do HighLoad++ e ouvi que só o Tarantool foi escrito em Lua. Isso não é verdade, muitas coisas estão escritas em Lua. Exemplos: OpenResty, servidor Prosody XMPP, mecanismo de jogo Love2D, Lua com script em Warcraft e outros. Existem muitos desenvolvedores Lua, eles têm uma comunidade grande e responsiva. Todas as minhas dúvidas sobre Lua foram resolvidas em poucas horas. Quando você escreve para a lista de discussão, em apenas alguns minutos já há um monte de respostas descrevendo o que e como, o que é o quê. É ótimo. Infelizmente, tal comunidade espiritual não está em toda parte.
Existe o GitHub para OpenResty, onde você pode abrir um problema se algo estiver quebrado. Há uma lista de e-mails nos Grupos do Google, onde você pode discutir questões gerais, há uma lista de e-mails em chinês - nunca se sabe, talvez você não fale inglês, mas sabe chinês.
Resultados de
- Espero ter conseguido transmitir que o OpenResty é uma estrutura muito conveniente, adaptada para a web.
- Possui baixa barreira de entrada, pois o código é semelhante ao que escrevemos, a linguagem é bastante simples e minimalista.
- Ele fornece E/S assíncrona sem retornos de chamada, não teremos nenhum macarrão como às vezes podemos escrever em NodeJS.
- Tem fácil implantação, pois só precisamos do NGINX com o módulo necessário e nosso código, e tudo funciona na hora.
- Comunidade grande e responsiva.
Não contei em detalhes como é feito o roteamento, acabou sendo uma história muito longa.
Obrigado!

Fonte: habr.com
