Volvemos a publicar la transcripción del informe de la conferencia. 2016, que se celebró en Skolkovo, cerca de Moscú, los días 7 y 8 de noviembre del año pasado. explica cómo ampliar la funcionalidad de NGINX con OpenResty y Lua.
Hola a todos, mi nombre es Vladimir Protasov, trabajo para Parallels. Les contaré un poco sobre mí. Paso tres cuartas partes de mi vida escribiendo código. Me convertí en programador hasta la médula en el sentido literal: a veces veo código en mis sueños. Una cuarta parte de la vida es desarrollo industrial, escribir código que pasa directamente a producción. Código que algunos de ustedes usan pero no lo saben.
Para hacerte saber lo malo que fue. Cuando era pequeño, entré y me dieron estas bases de datos de dos terabytes. Ahora está aquí para todos. Fui a conferencias y pregunté: “Chicos, díganme, ¿tienen big data? ¿Está todo bien? ¿Cuántas bases tienes ahí? Me respondieron: "¡Tenemos 100 gigabytes!" Dije: "¡Genial, 100 gigabytes!" Y pensé para mis adentros cómo salvar limpiamente la cara de póquer. Piensas, sí, los chicos son geniales, y luego regresas y juegas con estas bases de datos de varios terabytes. Y esto es ser junior. ¿Te imaginas el éxito que es?
Conozco más de 20 lenguajes de programación. Esto es lo que tuve que descubrir en el curso del trabajo. Te dan código en Erlang, en C, en C++, en Lua, en Python, en Ruby, en alguna otra cosa, y tienes que cortarlo todo. En general, tuve que hacerlo. No fue posible calcular el número exacto, pero alrededor de 20 el número se perdió.
Como todos aquí saben qué es Parallels y qué hacemos, no hablaré de lo geniales que somos y lo que hacemos. Sólo te diré que tenemos 13 oficinas en todo el mundo, más de 300 empleados, desarrollo en Moscú, Tallin y Malta. Si lo desea, puede trasladarse a Malta, si hace frío en invierno y necesita calentarse la espalda.
Específicamente, nuestro departamento escribe en Python 2. Estamos en el negocio y no tenemos tiempo para introducir tecnologías de moda, por lo que sufrimos. Tenemos Django, porque lo tiene todo, y lo que sobra lo cogimos y lo tiramos. También MySQL, Redis y NGINX. También tenemos muchas otras cosas interesantes. Tenemos MongoDB, tenemos conejos corriendo por ahí, simplemente no tenemos nada, pero no es mío y yo no lo hago.
OpenResty
Le hablé de mí. Veamos de qué voy a hablar hoy:
- ¿Qué es OpenResty y con qué se come?
- ¿Por qué reinventar la rueda cuando tenemos Python, NodeJS, PHP, Go y otras cosas interesantes con las que todos están contentos?
- Y algunos ejemplos de la vida real. Tuve que recortar mucho el informe porque lo recibí durante 3,5 horas, por lo que habrá pocos ejemplos.
OpenResty es NGINX. Gracias a él, tenemos un servidor web completo, bien escrito y que funciona rápido. Creo que la mayoría de nosotros usamos NGINX en producción. Todos sabéis que es rápido y genial. Hicieron E/S sincrónicas geniales en él, por lo que no necesitamos ciclar nada de la misma manera que se cicló gevent en Python. Gevent es genial, genial, pero si escribes código C y algo sale mal con gevent, te volverás loco depurándolo. Tenía experiencia: me llevó dos días enteros descubrir qué salió mal allí. Si alguien no hubiera investigado durante unas semanas antes, hubiera encontrado el problema, lo hubiera escrito en Internet y Google no lo hubiera encontrado, nos habríamos vuelto completamente locos.
NGINX ya realiza almacenamiento en caché y contenido estático. No necesita preocuparse por cómo hacerlo humanamente, para no disminuir la velocidad en alguna parte, para no perder descriptores en alguna parte. Nginx es muy conveniente de implementar, no necesita pensar en qué llevar: WSGI, PHP-FPM, Gunicorn, Unicorn. Se instaló Nginx y se lo entregó a los administradores, ellos saben cómo trabajar con él. Nginx procesa las solicitudes de forma estructurada. Hablaré de esto un poco más tarde. En definitiva, tiene una fase en la que acaba de aceptar la solicitud, en la que la procesa y en la que entrega el contenido al usuario.
Nginx es genial, pero hay un problema: no es lo suficientemente flexible incluso con todas esas características interesantes que los chicos incluyeron en la configuración, a pesar de que se puede personalizar. Este poder no es suficiente. Por lo tanto, los chicos de Taobao alguna vez, creo que hace unos ocho años, incorporaron Lua. ¿Qué da?
- tamaño. Es pequeño. LuaJIT ofrece entre 100 y 200 kilobytes de sobrecarga de memoria y una sobrecarga de rendimiento mínima.
- velocidad. El intérprete LuaJIT está cerca de C en muchas situaciones, en algunas situaciones pierde frente a Java, en otras lo supera. Durante un tiempo, se consideró lo último en tecnología, el mejor compilador JIT. Ahora los hay más chulos, pero muy pesados, por ejemplo, el mismo V8. Algunos intérpretes de JS y Java HotSpot son más rápidos en algunos puntos, pero aún pierden en algunos puntos.
- Fácil de aprender. Si tiene, digamos, un código base de Perl y no está en Booking, no encontrará programadores de Perl. Como no están, se los llevaron a todos y es largo y difícil enseñarles. Si desea programadores para otra cosa, es posible que también sea necesario volver a capacitarlos o encontrarlos. En el caso de Lua, todo es sencillo. Cualquier joven puede aprender Lua en tres días. Me llevó unas dos horas darme cuenta. Dos horas después, ya estaba escribiendo código en producción. Aproximadamente una semana después, fue directamente a producción y se fue.
Como resultado, se ve así:

Hay mucho aquí. OpenResty ha ensamblado un montón de módulos, tanto luash como motores. Y tendrá todo listo: implementado y funcionando.
Примеры
Basta de letras, pasemos al código. Aquí hay un pequeño Hola Mundo:

¿Lo que está ahí? esta es la ubicación de los motores. No nos preocupamos, no escribimos nuestra propia ruta, no tomamos ninguna ya preparada: ya la tenemos en NGINX, vivimos bien y con pereza.
content_by_lua_block es un bloque que dice que estamos entregando contenido usando un script Lua. Tomamos una variable de motores. remote_addr y deslizarlo en string.format... Esto es lo mismo que sprintf, solo en Lua, solo correcto. Y se lo damos al cliente.
Como resultado, se verá así:

Pero volvamos al mundo real. En producción, nadie implementa Hello World. Nuestra aplicación suele ir a la base de datos o a algún otro lugar y la mayor parte del tiempo espera una respuesta.

Simplemente se sienta y espera. No es muy bueno. Cuando vienen 100.000 usuarios, es muy difícil para nosotros. Por lo tanto, usemos una aplicación simple como ejemplo. Buscaremos imágenes, por ejemplo, de gatos. Solo que no solo buscaremos, ampliaremos las palabras clave y, si el usuario buscó "gatitos", encontraremos gatos, peludos, etc. Primero necesitamos obtener los datos de la solicitud en el backend. Se parece a esto:

Dos líneas le permiten seleccionar parámetros GET, sin complicaciones. Luego, por ejemplo, obtenemos esta información de una base de datos con una tabla por palabra clave y extensión mediante una consulta SQL normal. Todo es sencillo. Se parece a esto:

Conectamos la biblioteca resty.mysql, que ya tenemos en el kit. No necesitamos instalar nada, todo está listo. Especifique cómo conectarse y realizar una consulta SQL:

Da un poco de miedo, pero funciona. Aquí 10 es el límite. Sacamos 10 discos, nos da pereza, no queremos mostrar más. En SQL, me olvidé del límite.
Luego encontramos imágenes para todas las consultas. Recopilamos un montón de solicitudes y completamos una tabla Lua llamada reqs, y hacer ngx.location.capture_multi.

Todas estas solicitudes van en paralelo y nos devuelven las respuestas. El tiempo de ejecución es igual al tiempo de respuesta del más lento. Si todos respondemos en 50 milisegundos y enviamos cien solicitudes, recibiremos una respuesta en 50 milisegundos.
Como somos vagos y no queremos escribir manejo y almacenamiento en caché HTTP, haremos que NGINX haga todo por nosotros. Como viste, hubo una solicitud de url/fetch, aquí está:

hacemos simple proxy_pass, especifica dónde almacenar en caché, cómo hacerlo y todo funciona para nosotros.
Pero esto no es suficiente, todavía necesitamos darle los datos al usuario. La idea más sencilla es serializar todo en JSON, fácilmente, en dos líneas. Le damos Content-Type, le damos JSON.
Pero hay una dificultad: el usuario no quiere leer JSON. Necesitamos atraer desarrolladores de aplicaciones para el usuario. A veces al principio no tenemos ganas de hacerlo. Sí, y los especialistas en SEO dirán que si buscamos imágenes, no les importa. Y si les damos algún contenido, dirán que nuestros buscadores no indexan nada.
¿Qué hacer con ello? Por supuesto, le daremos HTML al usuario. Generar con identificadores no es algo común, por eso queremos usar plantillas. Hay una biblioteca para esto. lua-resty-template.

Debes haber visto las tres temidas letras OPM. OpenResty viene con su propio administrador de paquetes, a través del cual puede instalar un montón de módulos diferentes, en particular, lua-resty-template. Es un motor de plantillas simple similar a las plantillas de Django. Allí puedes escribir código y realizar sustitución de variables.
Como resultado, todo se verá así:

Tomamos los datos y volvimos a representar la plantilla en dos líneas. El usuario está contento, tiene gatos. Desde que ampliamos la solicitud, también recibió un lobo marino para gatitos. Nunca se sabe, tal vez lo estaba buscando, pero no pudo formular su solicitud correctamente.
Todo está bien, pero estamos en desarrollo y no queremos mostrárselo a los usuarios todavía. Hagamos una autorización. Para hacer esto, veamos cómo NGINX maneja la solicitud en términos de OpenResty:
- Primera fase - de la máquina, cuando el usuario acaba de llegar y lo miramos por encabezados, por dirección IP, por otros datos. Podemos cortarlo inmediatamente si no nos gusta. Esto se puede usar para autorización o, si recibimos muchas solicitudes, podemos cortarlas fácilmente en esta fase.
- volver a escribir. Reescribiendo algunos datos de la solicitud.
- contenido. Damos contenido al usuario.
- filtro de encabezado. Cambie los encabezados de respuesta. si usáramos
proxy_pass, podemos reescribir algunos encabezados antes de entregárselos al usuario. - filtro de cuerpo. Podemos cambiar el cuerpo.
- log - Inicio sesión. Es posible escribir registros en elasticsearch sin una capa adicional.
Nuestra autorización se verá así:

Lo agregaremos a eso location, que describimos antes, y colocamos el siguiente código allí:

Miramos para ver si tenemos un token de cookie. Si no, entonces solicitamos la autorización. Los usuarios son astutos y pueden adivinar que es necesario configurar un token de cookie. Por tanto, también lo pondremos en Redis:

El código para trabajar con Redis es muy simple y no se diferencia del de otros lenguajes. Al mismo tiempo, todas las entradas / salidas, lo que hay allí, lo que hay aquí, no se bloquea. Si escribe código sincrónico, funciona de forma asincrónica. Como con gevent, sólo que bien hecho.

Hagamos la autorización en sí:

Decimos que necesitamos leer el cuerpo de la solicitud. Recibimos argumentos POST, verificamos que el nombre de usuario y la contraseña sean correctos. Si es incorrecto, activamos la autorización. Y si son correctos, entonces escribimos el token en Redis:

No olvides configurar la cookie, esto también se hace en dos líneas:

El ejemplo es simple, especulativo. Por supuesto, no haremos un servicio que muestre gatos a la gente. Pero quién nos conoce. Entonces, repasemos lo que se puede hacer en producción.
- backend minimalista. A veces necesitamos enviar bastantes datos al backend: en algún lugar debemos sustituir la fecha, en algún lugar debemos mostrar algún tipo de lista, decir cuántos usuarios hay en el sitio ahora, atornillar un contador o estadísticas. Algo tan pequeño. Algunas piezas mínimas se pueden hacer muy fácilmente. Esto será rápido, fácil y genial.
- Preprocesamiento de datos. A veces queremos insertar anuncios en nuestra página y tomamos estos anuncios con solicitudes de API. Esto es muy fácil de hacer aquí. No cargamos nuestro backend, que ya está trabajando duro. Puedes recoger y recoger aquí. Podemos moldear algún JS o por el contrario despegar, preprocesar algo antes de entregárselo al usuario.
- Fachada para microservicio. Este también es un muy buen caso, lo implementé. Antes de eso, trabajé para Tenzor, una empresa de informes electrónicos que proporciona informes a aproximadamente la mitad de las entidades legales del país. Hemos creado un servicio, allí se hacen muchas cosas usando el mismo mecanismo: enrutamiento, autorización y más.
OpenResty se puede utilizar como pegamento para sus microservicios para proporcionar un acceso único a todo y una interfaz única. Dado que los microservicios se pueden escribir de tal manera que tenga Node.js aquí, PHP aquí, Python aquí, algo de Erlang aquí, entendemos que no queremos reescribir el mismo código en todas partes. Por lo tanto, OpenResty se puede conectar al frente. - Estadísticas y análisis. Por lo general, NGINX está en la entrada y todas las solicitudes pasan por allí. Es en este lugar donde es muy conveniente recolectar. Puede calcular algo inmediatamente y tirarlo a algún lugar, por ejemplo, el mismo Elasticsearch, Logstash, o simplemente escribirlo en el registro y luego enviarlo a algún lugar.
- Sistemas multiusuario. Por ejemplo, los juegos en línea también son muy buenos. Hoy en Ciudad del Cabo, Alexander Gladysh le dirá cómo crear rápidamente un prototipo de un juego multijugador utilizando OpenResty.
- Solicitar filtrado (WAF). Ahora está de moda fabricar todo tipo de firewalls para aplicaciones web, existen muchos servicios que los proporcionan. Con la ayuda de OpenResty, puede crear un firewall de aplicaciones web que filtrará las solicitudes de forma sencilla y sencilla según sus necesidades. Si tiene Python, entonces comprende que PHP definitivamente no se le inyectará, a menos, por supuesto, que lo genere en cualquier lugar desde la consola. Sabes que tienes MySQL y Python. Probablemente, aquí puedan intentar hacer algún tipo de recorrido de directorio e inyectar algo en la base de datos. Por lo tanto, puede filtrar solicitudes tontas de forma rápida y económica desde el principio.
- Comunidad. Dado que OpenResty se basa en NGINX, tiene una ventaja: esta es Comunidad NGINX. Es muy grande y muchas de las preguntas que tendrá al principio ya han sido respondidas por la comunidad NGINX.
desarrolladores lua. Ayer hablé con los chicos que asistieron al día de capacitación de HighLoad ++ y escuché que solo Tarantool está escrito en Lua. Esto no es así, muchas cosas están escritas en Lua. Ejemplos: OpenResty, servidor Prosody XMPP, motor de juego Love2D, Lua está escrito en Warcraft y otros lugares. Hay muchos desarrolladores de Lua, tienen una comunidad grande y receptiva. Todas mis preguntas sobre Lua fueron respondidas en unas pocas horas. Cuando escribes en la lista de correo, literalmente en unos minutos ya tienes un montón de respuestas, describen qué y cómo, qué es qué. Es genial. Desafortunadamente, una comunidad tan amable y sincera no se encuentra en todas partes.
OpenResty tiene GitHub, donde puedes abrir un problema si algo se rompe. Hay una lista de correo en Grupos de Google donde puedes discutir temas generales, hay una lista de correo en chino; nunca se sabe, tal vez no hables inglés, pero tienes conocimientos de chino.
resultados
- Espero haber podido transmitir que OpenResty es un marco web muy conveniente.
- Tiene un umbral de entrada bajo, ya que el código es similar al que escribimos, el lenguaje es bastante simple y minimalista.
- Proporciona E/S asincrónicas sin devoluciones de llamada, no tendremos fideos como a veces podemos escribir en NodeJS.
- Tiene una implementación fácil, porque solo necesitamos NGINX con el módulo correcto y nuestro código, y todo funciona de inmediato.
- Comunidad grande y receptiva.
No conté en detalle cómo se realiza el enrutamiento; resultó ser una historia muy larga.
Gracias por su atención!

Fuente: habr.com
