Tres bibliotecas Lua Resty comúnmente utilizadas en OpenResty
API7.ai
January 13, 2023
Aprender sobre lenguajes de programación y plataformas a menudo se trata de comprender las bibliotecas estándar y de terceros en lugar de la sintaxis en sí. Después de aprender su API y técnicas de optimización de rendimiento, necesitamos aprender el uso de varias bibliotecas lua-resty
para extender nuestras capacidades de OpenResty a más escenarios.
¿Dónde encontrar la biblioteca lua-resty
?
En comparación con PHP, Python y JavaScript, las bibliotecas estándar y de terceros de OpenResty actualmente son relativamente escasas, y encontrar las bibliotecas lua-resty
adecuadas no es fácil. Sin embargo, aquí hay dos fuentes recomendadas para ayudarte a encontrarlas más rápido.
La primera recomendación es el repositorio awesome-resty
mantenido por Aapo. Este repositorio organiza las bibliotecas relacionadas con OpenResty por categoría y es muy completo, incluyendo módulos C de NGINX, bibliotecas lua-resty
, frameworks web, bibliotecas de enrutamiento, plantillas, frameworks de pruebas, etc. Es tu primera opción para recursos de OpenResty.
Si no encuentras la biblioteca adecuada en el repositorio de Aapo, también puedes buscar en luarocks
, topm
o GitHub. Podría haber algunas bibliotecas que no han sido de código abierto por mucho tiempo y no han recibido mucha atención.
En los artículos anteriores, hemos aprendido sobre varias bibliotecas útiles como lua-resty-mlcache
, lua-resty-traffic
, lua-resty-shell
, etc. Hoy, en el último artículo de la sección de optimización de rendimiento de OpenResty, conoceremos 3 bibliotecas periféricas más únicas, todas contribuidas por desarrolladores de la comunidad.
Mejora de rendimiento de ngx.var
Primero, veamos un módulo C: lua-var-nginx-module
. Como mencioné anteriormente, ngx.var
es una operación relativamente costosa en términos de rendimiento. Por lo tanto, en la práctica, necesitamos usar ngx.ctx
como una capa de caché.
Entonces, ¿hay alguna manera de resolver completamente el problema de rendimiento de ngx.var
?
Este módulo C hace algunos experimentos en esta área, y los resultados son notables, con una mejora de rendimiento de 5 veces sobre ngx.var
. Utiliza el enfoque FFI
, por lo que primero necesitas compilar OpenResty con la siguiente opción de compilación.
./configure --prefix=/opt/openresty \
--add-module=/path/to/lua-var-nginx-module
Luego usa luarocks
para instalar la biblioteca lua de la siguiente manera:
luarocks install lua-resty-ngxvar
El método llamado aquí también es muy simple, solo requiere una línea de la función fetch
. Funciona igual que el original ngx.var.remote_addr
para obtener la dirección IP del cliente.
content_by_lua_block {
local var = require("resty.ngxvar")
ngx.say(var.fetch("remote_addr"))
}
Después de entender estas operaciones básicas, es posible que tengas más curiosidad sobre cómo este módulo logra una mejora significativa de rendimiento. Como siempre decimos, "no hay secretos frente al código fuente". Así que descubramos cómo obtener la variable remote_addr
.
ngx_int_t
ngx_http_lua_var_ffi_remote_addr(ngx_http_request_t *r, ngx_str_t *remote_addr)
{
remote_addr->len = r->connection->addr_text.len;
remote_addr->data = r->connection->addr_text.data;
return NGX_OK;
}
Después de leer este código, verás que este enfoque Lua FFI
es el mismo que el enfoque de lua-resty-core
. Tiene la ventaja obvia de usar FFI
para obtener variables directamente, evitando la lógica de búsqueda original de ngx.var
. Su desventaja es obvia: agregar funciones C y llamadas FFI
para cada variable que deseas obtener, lo cual consume tiempo y energía.
Algunos podrían preguntar: "¿Por qué diría que esto consume tiempo y energía? ¿No parece bastante sustancial el código C anterior?" Echemos un vistazo al origen de estas líneas de código, que provienen de src/http/ngx_http_variables.c
en el código de NGINX.
static ngx_int_t
ngx_http_variable_remote_addr(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
v->len = r->connection->addr_text.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = r->connection->addr_text.data;
return NGX_OK;
}
¡Después de ver el código fuente, el misterio se revela! lua-var-nginx-module
es un portador del código de variables de NGINX, con un envoltorio FFI en la capa externa, y de esta manera logra la optimización de rendimiento. Esta es una buena idea y una buena dirección para la optimización.
Al aprender una biblioteca o una herramienta, no debemos detenernos solo en el nivel de operación, sino también preguntarnos por qué lo hacemos y mirar el código fuente. Por supuesto, también te animo a contribuir con código para apoyar más variables de NGINX.
JSON Schema
Aquí presento una biblioteca lua-resty
: lua-rapidjson
. Es un envoltorio alrededor de rapidjson
, la biblioteca JSON de código abierto de Tencent, conocida por su rendimiento. Aquí, nos enfocamos en la diferencia entre ella y cjson
: el soporte de JSON Schema
.
JSON Schema
es un estándar común que nos permite describir con precisión el formato de los parámetros en una interfaz y cómo deben validarse. Aquí hay un ejemplo simple:
"stringArray": {
"type": "array",
"items": { "type": "string" },
"minItems": 1,
"uniqueItems": true
}
Este JSON describe con precisión que el parámetro stringArray
es de tipo array de cadenas y que el array no puede estar vacío, ni los elementos del array pueden duplicarse.
lua-rapidjson
nos permite usar JSON Schema en OpenResty, lo que puede traer una gran conveniencia a la validación de interfaces. Por ejemplo, para la interfaz de límite de conteo descrita anteriormente, podemos usar el siguiente esquema para describir:
local schema = {
type = "object",
properties = {
count = {type = "integer", minimum = 0},
time_window = {type = "integer", minimum = 0},
key = {type = "string", enum = {"remote_addr", "server_addr"}},
rejected_code = {type = "integer", minimum = 200, maximum = 600},
},
additionalProperties = false,
required = {"count", "time_window", "key", "rejected_code"},
}
Encontrarás que esto puede llevar a dos beneficios muy obvios:
- Para el front-end, el front-end puede reutilizar directamente esta descripción de esquema para el desarrollo de páginas frontales y la validación de parámetros sin preocuparse por el back-end.
- Para el back-end, el back-end usa directamente la función de validación de esquema
SchemaValidator
delua-rapidjson
para determinar la legitimidad de la interfaz, y no es necesario escribir código adicional.
Comunicación entre Worker
s
Finalmente, me gustaría hablar sobre la biblioteca lua-resty
que permite la comunicación entre workers en OpenResty, donde no existe un mecanismo de comunicación directa entre workers, lo que plantea muchos problemas. Imaginemos un escenario:
Un servicio de OpenResty tiene 24 procesos worker, y cuando el administrador actualiza una configuración del sistema a través de las APIs REST HTTP, solo un
Worker
recibe la actualización del administrador y escribe el resultado en la base de datos, actualizando elshared dict
y lalru cache
dentro de su propio Worker. Entonces, ¿cómo pueden los otros 23 workers ser notificados para actualizar esta configuración?
Se necesita un mecanismo de notificación entre múltiples Worker
s para realizar la tarea anterior. En el caso de que OpenResty no lo soporte, tenemos que salvar el día usando datos de shared dict
entre workers.
lua-resty-worker-events
es una implementación concreta de esta idea. Mantiene un número de versión en un shared dict
, y cuando se publica un nuevo mensaje, se suma uno al número de versión y se coloca el contenido del mensaje en el diccionario con el número de versión como key
.
event_id, err = _dict:incr(KEY_LAST_ID, 1)
success, err = _dict:add(KEY_DATA .. tostring(event_id), json)
Además, se crea un bucle de polling
con un intervalo predeterminado de 1 segundo en segundo plano usando ngx.timer
para verificar constantemente los cambios en el número de versión:
local event_id, err = get_event_id()
if event_id == _last_event then
return "done"
end
De esta manera, tan pronto como se encuentra una nueva notificación de evento para procesar, se recupera el contenido del mensaje del shared dict
basado en el número de versión:
while _last_event < event_id do
count = count + 1
_last_event = _last_event + 1
data, err = _dict:get(KEY_DATA..tostring(_last_event))
end
En general, aunque lua-resty-worker-events
tiene un retraso de un segundo, aún implementa un mecanismo de notificación de eventos entre Workers.
Sin embargo, en algunos escenarios en tiempo real, como la mensajería push, la falta de comunicación directa entre procesos Worker
de OpenResty puede causarte algunos problemas. No hay una solución mejor para esto, pero si tienes buenas ideas, no dudes en discutirlas en Github. Muchas características de OpenResty son impulsadas por la comunidad para construir un ciclo ecológico virtuoso.
Resumen
Las tres bibliotecas que presentamos hoy son únicas y traen más posibilidades para las aplicaciones de OpenResty. Finalmente, un tema interactivo: ¿has encontrado alguna biblioteca interesante alrededor de OpenResty? ¿O qué encuentras o te preguntas sobre las bibliotecas mencionadas hoy? Te invitamos a enviar este artículo a los usuarios de OpenResty a tu alrededor para intercambiar y progresar juntos.