OpenResty: transformamos NGINX en un servidor de aplicaciones completo

OpenResty: transformamos NGINX en un servidor de aplicaciones completoPublicamos nuevamente la transcripción de la presentación de la conferencia HighLoad++. 2016, que tuvo lugar en Skolkovo, cerca de Moscú, del 7 al 8 de noviembre del año pasado. Vladimir Protasov nos cuenta cómo ampliar la funcionalidad de NGINX con OpenResty y Lua.

Hola a todos, me llamo Vladimir Protasov, trabajo en Parallels. Voy a contar un poco sobre mí. Durante tres cuartas partes de mi vida, he estado escribiendo código. Me convertí en programador hasta la médula: a veces sueño con código. La otra cuarta parte de mi vida está dedicada al desarrollo industrial, escribiendo código que va directamente a producción. Código que algunos de ustedes utilizan sin darse cuenta.

Para que entiendan cuán mal estaban las cosas. Cuando era un junior pequeño, llegué y me dieron bases de datos de dos terabytes. Ahora todos tienen highload aquí. Asistí a conferencias y preguntaba: 'Chicos, cuéntenme, ¿tienen big data, es genial? ¿Cuánto tienen en las bases?' Me respondían: '¡Tenemos 100 gigabytes!' Yo decía: '¡Genial, 100 gigabytes!' Pero por dentro pensaba, cómo mantener una expresión indiferente. Pensabas, sí, estos chicos son geniales, y luego regresabas y te dedicabas a luchar con estas bases de datos de múltiples terabytes. Y eso, siendo un junior. ¿Pueden imaginar qué golpe es eso?

Conozco más de 20 lenguajes de programación. Esto es algo con lo que tuve que lidiar en el trabajo. Te dan código en Erlang, C, C++, Lua, Python, Ruby y algo más, y tienes que hacer todo eso. En general, fue necesario. No pude contar el número exacto, pero perdí la cuenta alrededor de 20.

Como todos los presentes saben qué es Parallels y en qué estamos involucrados, no hablaré sobre lo geniales que somos y qué hacemos. Solo mencionaré que tenemos 13 oficinas en todo el mundo, más de 300 empleados, y el desarrollo se lleva a cabo en Moscú, Tallin y Malta. Si lo deseas, puedes mudarte a Malta si en invierno hace frío y necesitas calentar la espalda.

Nuestro departamento específicamente escribe en Python 2. Nos dedicamos a los negocios y no tenemos tiempo para implementar tecnologías de moda, así que sufrimos. Usamos Django porque tiene todo lo que necesitamos, y descartamos lo superfluo. También utilizamos MySQL, Redis y NGINX. Además, tenemos muchas otras cosas geniales. Contamos con MongoDB, corren conejos, tenemos de todo, pero eso no es mi campo y no me ocupo de ello.

OpenResty

Ya he hablado de mí. Ahora veamos de qué tratará mi charla hoy:

  • ¿Qué es OpenResty y para qué se utiliza?
  • ¿Para qué inventar otra bicicleta cuando ya tenemos Python, NodeJS, PHP, Go y otras cosas geniales que todos adoran?
  • Y un par de ejemplos de la vida real. Tuve que recortar mucho mi presentación porque me salía a 3,5 horas, así que habrá pocos ejemplos.

OpenResty es NGINX. Gracias a él contamos con un servidor web completo, bien escrito y rápido. Creo que la mayoría de nosotros usamos NGINX en producción. Todos saben que es rápido y genial. Tiene entrada/salida asíncrona excepcional, así que no necesitamos reinventar la rueda como lo hizo Python con gevent. Gevent es genial, impresionante, pero si escribes código en C y algo sale mal, te volverás loco depurándolo. Yo tuve esa experiencia: me tomó dos días darme cuenta de qué estaba mal. Si alguien no hubiera estado investigando durante semanas, no hubiera encontrado el problema, no lo hubiera escrito en internet y Google no lo hubiera encontrado, nos habríamos vuelto locos.

NGINX ya tiene caché y contenido estático. No necesitas preocuparte por cómo hacerlo de manera correcta para no atorar nada o perder descriptores. Nginx es muy fácil de desplegar, no necesitas pensar en qué elegir: WSGI, PHP-FPM, Gunicorn, Unicorn. Instalaste Nginx, lo entregaste a los administradores, ellos saben cómo trabajar con esto. Nginx maneja las solicitudes de forma estructurada. Te hablaré un poco más sobre esto más adelante. En resumen, hay una fase en la que acepta la solicitud, otra en la que la procesa y por último entrega el contenido al usuario.

Nginx es genial, pero hay un problema: no es lo suficientemente flexible incluso con todas esas características geniales que los chicos incorporaron en la configuración. Esta potencia se queda corta. Por eso, hace unos ocho años, los chicos de Taobao integraron Lua. ¿Qué ofrece esto?

  • Tamaño. Es pequeño. LuaJIT tiene un sobrecoste de aproximadamente 100-200 kilobytes en memoria y un mínimo sobrecoste en rendimiento.
  • Velocidad. El intérprete LuaJIT, en muchas situaciones, se acerca a C, en algunas situaciones pierde contra Java, y en ciertas ocasiones la supera. Durante un tiempo fue considerado el mejor JIT compilador. Ahora hay opciones más avanzadas, pero son muy pesadas, como V8. Algunos intérpretes de JS y el HotSpot de Java son más rápidos en ciertos puntos, pero en otros lugares aún quedan atrás.
  • Facilidad de uso. Si, por ejemplo, tienes una base de código en Perl, y no eres Booking, no encontrarás programadores de Perl. Porque no hay, todos fueron llevados, y enseñarles lleva tiempo y es complicado. Si quieres programadores en algo diferente, quizás también tendrás que reciclarlos o encontrarlos. En el caso de Lua, es sencillo. Cualquier principiante puede aprender Lua en tres días. A mí me tomó alrededor de dos horas entenderlo. Después de dos horas ya estaba escribiendo código en producción. Aproximadamente una semana después, ya estaba funcionando en producción.

Como resultado, se ve así:

OpenResty: transformamos NGINX en un servidor de aplicaciones completo

Hay mucho aquí. En OpenResty han reunido un montón de módulos, tanto de Lua como del motor. Y tú tienes todo listo: lo despliegas y funciona.

Ejemplos

Suficiente de poesía, pasemos al código. Aquí hay un pequeño Hello World:

OpenResty: transformamos NGINX en un servidor de aplicaciones completo

¿Qué hay aquí? es una ubicación de motor. No nos preocupamos, no escribimos nuestro propio enrutamiento, no tomamos ninguno ya hecho; ya tenemos en NGINX, vivimos bien y con pereza.

content_by_lua_block – es un bloque que indica que estamos entregando contenido usando un script de Lua. Tomamos la variable del motor remote_addr y la insertamos en string.format. Es lo mismo que, sprintf, solo que en Lua, solo que correcto. Y se lo entregamos al cliente.

Como resultado, se verá así:

OpenResty: transformamos NGINX en un servidor de aplicaciones completo

Pero volvamos al mundo real. En producción, nadie despliega Hello World. Nuestra aplicación normalmente accede a una base de datos o a algún otro lugar y la mayor parte del tiempo está esperando una respuesta.

OpenResty: transformamos NGINX en un servidor de aplicaciones completo

Simplemente se sienta y espera. Esto no es muy bueno. Cuando llegan 100,000 usuarios, se vuelve muy difícil. Por lo tanto, propongamos un simple ejemplo de aplicación. Buscaremos imágenes, por ejemplo, de gatitos. Solo que no buscaremos así, ampliaremos las palabras clave y, si el usuario busca 'gatitos', le encontraremos gatitos, peluditos y demás. Para empezar, necesitamos obtener los datos de la solicitud en el backend. Se ve así:

OpenResty: transformamos NGINX en un servidor de aplicaciones completo

Dos líneas te permiten obtener los parámetros GET, sin complicaciones. Luego, por ejemplo, desde la base de datos con una tabla de palabras clave y extensiones obtenemos esta información mediante una consulta SQL normal. Es simple. Se ve así:

OpenResty: transformamos NGINX en un servidor de aplicaciones completo

Conectamos la biblioteca resty.mysql, que ya tenemos en el paquete. No necesitamos instalar nada, todo está listo. Indicamos cómo conectarnos y hacemos la consulta SQL:

OpenResty: transformamos NGINX en un servidor de aplicaciones completo

Aquí se siente un poco aterrador, pero todo funciona. Aquí 10 es el límite. Extraemos 10 registros, somos perezosos, no queremos mostrar más. Olvidé sobre el límite en SQL.

A continuación, encontramos imágenes para todas las consultas. Reunimos un montón de consultas y llenamos una tabla Lua, que se llama reqs, y hacemos ngx.location.capture_multi.

OpenResty: transformamos NGINX en un servidor de aplicaciones completo

Todas estas consultas se envían en paralelo, y recibimos respuestas. El tiempo de trabajo es igual al tiempo de respuesta del más lento. Si todos responden en 50 milisegundos y enviamos un centenar de consultas, la respuesta nos llegará en 50 milisegundos.

Como somos perezosos y no queremos escribir la gestión de HTTP y caché, haremos que NGINX haga todo por nosotros. Como vieron, había una consulta a url/fetch, aquí está:

OpenResty: transformamos NGINX en un servidor de aplicaciones completo

Hacemos un simple proxy_pass, indicamos dónde caché y cómo hacerlo, y todo funciona.

Pero eso no es suficiente, también necesitamos entregar datos al usuario. La idea más simple es serializar todo en JSON, fácil, en dos líneas. Devolvemos el Content-Type, devolvemos JSON.

Pero hay una complicación: el usuario no quiere leer JSON. Necesitamos atraer a los frontenders. A veces no queremos hacer esto al principio. Y los SEO dirán que si estamos buscando imágenes, no les importa. Pero si les damos algún contenido, dirán que nuestros motores de búsqueda no indexan nada.

¿Qué hacer con esto? Por supuesto, vamos a entregar HTML al usuario. Generarlo manualmente no es práctico, así que queremos usar plantillas. Para esto hay una biblioteca lua-resty-template.

OpenResty: transformamos NGINX en un servidor de aplicaciones completo

Probablemente vieron tres letras temibles OPM. OpenResty viene con su propio gestor de paquetes, a través del cual se pueden instalar muchos otros módulos, en particular, lua-resty-template. Este es un motor de plantillas simple, similar a las plantillas de Django. Se puede escribir código y hacer sustitución de variables.

Como resultado, todo se verá aproximadamente así:

OpenResty: transformamos NGINX en un servidor de aplicaciones completo

Tomamos los datos y renderizamos la plantilla en dos líneas nuevamente. El usuario está feliz, recibió gatitos. Como ampliamos la consulta, también recibió un lobo marino. Quién sabe, tal vez estaba buscando específicamente eso, pero no pudo formular correctamente su consulta.

Todo genial, pero estamos en desarrollo, y no queremos mostrar nada a los usuarios todavía. Hagamos autorización. Para hacer esto, veamos cómo NGINX maneja la consulta en términos de OpenResty:

  • La primera fase es -token y, cuando el usuario acaba de llegar y lo observamos a través de los encabezados, la dirección IP y otros datos. Podemos desconectarlo de inmediato si no nos agrada. Esto se puede usar para la autorización, o si recibimos demasiadas solicitudes, podemos eliminarlas fácilmente en esta fase.
  • rewrite. Reescribimos algunos datos de la solicitud.
  • texto alternativo. Le proporcionamos contenido al usuario.
  • filtro de encabezados. Modificamos los encabezados de respuesta. Si hemos utilizado proxy_pass, podemos reescribir algunos encabezados antes de entregarlos al usuario.
  • filtro de cuerpo. Podemos modificar el cuerpo.
  • log — registro. Se pueden escribir registros en Elasticsearch sin capa adicional.

Nuestra autorización se verá aproximadamente así:

OpenResty: transformamos NGINX en un servidor de aplicaciones completo

Lo añadiremos a la ubicación, que hemos descrito anteriormente, y agregaremos este código:

OpenResty: transformamos NGINX en un servidor de aplicaciones completo

Verificamos si tenemos el token de cookie. Si no, redirigimos a la autorización. Los usuarios son astutos y pueden adivinar que necesitan establecer el token de cookie. Por lo tanto, también lo almacenaremos en Redis:

OpenResty: transformamos NGINX en un servidor de aplicaciones completo

El código para trabajar con Redis es muy simple y no se diferencia de otros lenguajes. Además, toda la entrada/salida, tanto allí como aquí, es no bloqueante. Si escribes código síncrono, funcionará de manera asíncrona. Es algo similar a gevent, solo que bien implementado.

OpenResty: transformamos NGINX en un servidor de aplicaciones completo

Hagamos la autorización en sí:

OpenResty: transformamos NGINX en un servidor de aplicaciones completo

Decimos que necesitamos leer el cuerpo de la solicitud. Obtenemos los argumentos POST, verificamos que el nombre de usuario y la contraseña sean correctos. Si son incorrectos, redirigimos a la autorización. Pero si son correctos, almacenamos el token en Redis:

OpenResty: transformamos NGINX en un servidor de aplicaciones completo

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

OpenResty: transformamos NGINX en un servidor de aplicaciones completo

Ejemplo simple y teórico. Por supuesto, no vamos a hacer un servicio que muestre gatitos a la gente. Aunque quién sabe. Así que echemos un vistazo a lo que se puede hacer en producción.

  • Backend minimalista. A veces necesitamos proporcionar solo unos pocos datos al backend: en algún lugar necesitamos insertar una fecha, en algún otro, mostrar una lista, decir cuántos usuarios hay en el sitio, adjuntar un contador o estadísticas. Algo pequeño. Se pueden hacer trozos mínimos muy fácilmente. Con esto, se puede lograr rápido, fácil y genial.
  • Preprocesamiento de datos. A veces queremos integrar anuncios en nuestra página, y obtenemos esos anuncios a través de solicitudes API. Esto es muy fácil de hacer aquí. No sobrecargamos nuestro backend, que ya trabaja duro. Podemos tomar y reunir aquí. Podemos crear algún JS o, por el contrario, desmontar algo, preprocesar algo antes de entregarlo al usuario.
  • Interfaz para microservicios. Este también es un muy buen caso, lo he implementado. Antes trabajaba en una empresa llamada Tenzor, que se ocupa de la contabilidad electrónica, proporcionando contabilidad a aproximadamente la mitad de las entidades legales del país. Creamos un servicio, y muchas de las funcionalidades allí están hechas utilizando este mismo mecanismo: enrutamiento, autorización y más.
    OpenResty se puede usar como pegamento para tus microservicios, que proporcionará acceso único a todo y una interfaz unificada. Dado que los microservicios pueden estar escritos de tal manera que aquí tienes Node.js, aquí PHP, aquí Python, y aquí hay algo en Erlang, entendemos que no queremos reescribir el mismo código en todas partes. Por lo tanto, OpenResty se puede integrar en el frontend.
  • Estadísticas y análisis. Normalmente, NGINX se coloca en la entrada, y todas las solicitudes pasan a través de él. Es en este lugar donde es muy conveniente recopilar datos. Se puede calcular algo de inmediato y enviarlo a algún lugar, por ejemplo, Elasticsearch, Logstash o simplemente registrar en log y luego enviarlo a algún lugar.
  • Sistemas multipleyado. Por ejemplo, los juegos en línea también se realizan muy bien. Hoy en Ciudad del Cabo, Alexander Gladys va a hablar sobre cómo prototipar rápidamente un juego multijugador utilizando OpenResty.
  • Filtrado de solicitudes (WAF). Actualmente está de moda crear firewalls para aplicaciones web, hay muchos servicios que los proporcionan. Con OpenResty puedes crear tu propio firewall para aplicaciones web que filtrará solicitudes de manera simple y eficaz según tus requisitos. Si tienes Python, sabes que PHP no se inyectará en tu sistema, a menos que lo inicies desde la consola. Sabes que tienes MySQL y Python. Puede que intenten hacer algún tipo de directory traversal e inyectar algo en la base de datos. Por lo tanto, se pueden filtrar solicitudes sospechosas de forma rápida y económica directamente en el frontend.
  • Comunidad. Dado que OpenResty está construido sobre NGINX, tiene una ventaja: es la comunidad de NGINXEs muy amplio, y una buena parte de las preguntas que se plantearán al principio ya han sido resueltas por la comunidad de NGINX.

    Desarrolladores de LuaAyer hablé con algunos chicos que asistieron al día de capacitación de HighLoad++ y escuché que solo Tarantool está escrito en Lua. Esto no es cierto, se ha escrito mucho en Lua. Ejemplos: OpenResty, servidor XMPP Prosody, motor de juegos Love2D, scripts de Lua en Warcraft y en otros lugares. Hay muchos desarrolladores de Lua, tienen una comunidad grande y receptiva. Todas mis preguntas sobre Lua se resolvieron en unas pocas horas. Cuando escribes a la lista de correos, literalmente en unos minutos ya hay un montón de respuestas, explican qué y cómo, qué a qué. Es genial. Lamentablemente, no en todas partes hay una comunidad tan solidaria.
    En OpenResty hay GitHub, donde se puede abrir un issue si algo se rompe. Hay una lista de correos en Google Groups donde se pueden discutir cuestiones generales, hay una lista en chino, por si acaso, quizás no dominas el inglés pero sabes chino.

Resultados

  • Espero haber logrado transmitir que OpenResty es un marco de trabajo muy conveniente, diseñado para la web.
  • Tiene un bajo umbral de entrada, ya que el código es similar a lo que escribimos, el lenguaje es bastante simple y minimalista.
  • Proporciona I/O asíncrono sin callbacks, no tendremos la confusión que a veces podemos escribir en NodeJS.
  • Su despliegue es sencillo, ya que solo necesitamos NGINX con el módulo necesario y nuestro código, y todo funciona de inmediato.
  • Una comunidad grande y receptiva.

No conté en detalles cómo se hace la enrutación, porque se volvió una historia muy larga.

¡Gracias por su atención!

Reproducir video

Vladimir Protasov - OpenResty: Transformando NGINX en un servidor de aplicaciones completo

Fuente: habr.com
Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster