Trois bibliothèques Lua Resty couramment utilisées dans OpenResty
API7.ai
January 13, 2023
Apprendre les langages de programmation et les plateformes est souvent une question de compréhension des bibliothèques standard et tierces plutôt que de la syntaxe elle-même. Après avoir appris son API et les techniques d'optimisation des performances, nous devons apprendre à utiliser diverses bibliothèques lua-resty
pour étendre nos capacités OpenResty à davantage de scénarios.
Où trouver la bibliothèque lua-resty
?
Comparé à PHP, Python et JavaScript, les bibliothèques standard et tierces actuelles d'OpenResty sont encore relativement pauvres, et trouver les bonnes bibliothèques lua-resty
n'est pas facile. Cependant, voici deux sources recommandées pour vous aider à les trouver plus rapidement.
La première recommandation est le dépôt awesome-resty
maintenu par Aapo. Ce dépôt organise les bibliothèques liées à OpenResty par catégorie et est très complet, incluant les modules C NGINX, les bibliothèques lua-resty
, les frameworks web, les bibliothèques de routage, les templates, les frameworks de test, etc. C'est votre premier choix pour les ressources OpenResty.
Si vous ne trouvez pas la bibliothèque appropriée dans le dépôt d'Aapo, vous pouvez également consulter luarocks
, topm
, ou GitHub. Il pourrait exister des bibliothèques qui n'ont pas été open-sourcées depuis longtemps et qui n'ont pas reçu beaucoup d'attention.
Dans les articles précédents, nous avons appris à connaître plusieurs bibliothèques utiles comme lua-resty-mlcache
, lua-resty-traffic
, lua-resty-shell
, etc. Aujourd'hui, dans le dernier article de la section sur l'optimisation des performances d'OpenResty, nous allons découvrir 3 autres bibliothèques périphériques uniques, toutes contribuées par des développeurs de la communauté.
Amélioration des performances de ngx.var
Tout d'abord, regardons un module C : lua-var-nginx-module
. Comme je l'ai mentionné précédemment, ngx.var
est une opération relativement coûteuse en termes de performances. Ainsi, en pratique, nous devons utiliser ngx.ctx
comme une couche de cache.
Existe-t-il un moyen de résoudre complètement le problème de performance de ngx.var
?
Ce module C fait quelques expériences dans ce domaine, et les résultats sont remarquables, avec une amélioration des performances de 5 fois par rapport à ngx.var
. Il utilise l'approche FFI
, donc vous devez d'abord compiler OpenResty avec l'option de compilation suivante.
./configure --prefix=/opt/openresty \
--add-module=/path/to/lua-var-nginx-module
Ensuite, utilisez luarocks
pour installer la bibliothèque lua de la manière suivante :
luarocks install lua-resty-ngxvar
La méthode appelée ici est également très simple, nécessitant seulement une ligne de fonction fetch
. Elle fonctionne de la même manière que l'original ngx.var.remote_addr
pour obtenir l'adresse IP du client.
content_by_lua_block {
local var = require("resty.ngxvar")
ngx.say(var.fetch("remote_addr"))
}
Après avoir compris ces opérations de base, vous pourriez être plus curieux de savoir comment ce module parvient à une amélioration significative des performances. Comme nous le disons toujours, "il n'y a pas de secrets devant le code source". Alors, découvrons comment récupérer 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;
}
Après avoir lu ce code, vous verrez que cette approche Lua FFI
est la même que celle de lua-resty-core
. Elle a l'avantage évident d'utiliser FFI
pour obtenir directement les variables, en contournant la logique de recherche originale de ngx.var
. Son inconvénient est évident : ajouter des fonctions C et des appels FFI
pour chaque variable que vous souhaitez obtenir, ce qui prend du temps et de l'énergie.
Certains pourraient demander, "Pourquoi dirais-je que cela prend du temps et de l'énergie ? Le code C ci-dessus ne semble-t-il pas assez substantiel ?" Regardons la source de ces lignes de code, qui proviennent de src/http/ngx_http_variables.c
dans le code 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;
}
Après avoir vu le code source, le mystère est révélé ! lua-var-nginx-module
est un porteur du code de variable NGINX, avec un emballage FFI à l'extérieur, et de cette manière, il parvient à optimiser les performances. C'est une bonne idée et une bonne direction pour l'optimisation.
Lorsque vous apprenez une bibliothèque ou un outil, vous ne devez pas vous arrêter au niveau de l'opération, mais aussi vous demander pourquoi nous le faisons et regarder le code source. Bien sûr, je vous encourage également vivement à contribuer du code pour supporter plus de variables NGINX.
JSON Schema
Ici, je présente une bibliothèque lua-resty
: lua-rapidjson
. C'est un wrapper autour de rapidjson
, la bibliothèque JSON open-source de Tencent, et est connue pour ses performances. Ici, nous nous concentrons sur la différence entre elle et cjson
: le support de JSON Schema
.
JSON Schema
est une norme commune qui nous permet de décrire précisément le format des paramètres dans une interface et comment ils doivent être validés. Voici un exemple simple :
"stringArray": {
"type": "array",
"items": { "type": "string" },
"minItems": 1,
"uniqueItems": true
}
Ce JSON décrit avec précision que le paramètre stringArray
est de type tableau de chaînes de caractères et que le tableau ne peut pas être vide, ni les éléments du tableau être dupliqués.
lua-rapidjson
nous permet d'utiliser JSON Schema dans OpenResty, ce qui peut apporter une grande commodité à la validation des interfaces. Par exemple, pour l'interface de limite de compte décrite précédemment, nous pouvons utiliser le schéma suivant pour la décrire :
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"},
}
Vous constaterez que cela peut conduire à deux avantages très évidents :
- Pour le front-end, le front-end peut directement réutiliser cette description de schéma pour le développement de pages frontales et la validation des paramètres sans avoir à se soucier du back-end.
- Pour le back-end, le back-end utilise directement la fonction de validation de schéma
SchemaValidator
delua-rapidjson
pour déterminer la légitimité de l'interface, et il n'est pas nécessaire d'écrire du code supplémentaire.
Communication entre Worker
Enfin, je voudrais parler de la bibliothèque lua-resty
qui permet la communication entre les workers dans OpenResty, où il n'y a pas de mécanisme de communication directe entre les workers, ce qui pose beaucoup de problèmes. Imaginons un scénario :
Un service OpenResty a 24 processus worker, et lorsque l'administrateur met à jour une configuration du système via les API REST HTTP, un seul
Worker
reçoit la mise à jour de l'administrateur et écrit le résultat dans la base de données, mettant à jour leshared dict
et lelru cache
dans son propre Worker. Alors, comment les 23 autres workers peuvent-ils être notifiés pour mettre à jour cette configuration ?
Un mécanisme de notification entre plusieurs Worker
s est nécessaire pour accomplir la tâche ci-dessus. Dans le cas où OpenResty ne le supporte pas, nous devons sauver la situation en utilisant les données shared dict
entre les workers.
lua-resty-worker-events
est une implémentation concrète de cette idée. Il maintient un numéro de version dans un shared dict
, et lorsqu'un nouveau message est publié, il ajoute un au numéro de version et place le contenu du message dans le dictionnaire avec le numéro de version comme clé
.
event_id, err = _dict:incr(KEY_LAST_ID, 1)
success, err = _dict:add(KEY_DATA .. tostring(event_id), json)
De plus, une boucle polling
avec un intervalle par défaut de 1 seconde est créée en arrière-plan en utilisant ngx.timer
pour vérifier constamment les changements dans le numéro de version :
local event_id, err = get_event_id()
if event_id == _last_event then
return "done"
end
De cette manière, dès qu'une nouvelle notification d'événement est trouvée à traiter, le contenu du message est récupéré du shared dict
en fonction du numéro de version :
while _last_event < event_id do
count = count + 1
_last_event = _last_event + 1
data, err = _dict:get(KEY_DATA..tostring(_last_event))
end
Globalement, bien que lua-resty-worker-events
ait un délai d'une seconde, il implémente tout de même un mécanisme de notification d'événements entre Worker.
Cependant, dans certains scénarios en temps réel, comme la diffusion de messages, le manque de communication directe entre les processus Worker
d'OpenResty peut vous causer des problèmes. Il n'y a pas de meilleure solution pour cela, mais si vous avez de bonnes idées, n'hésitez pas à en discuter sur Github. De nombreuses fonctionnalités d'OpenResty sont motivées par la communauté pour construire un cycle écologique vertueux.
Résumé
Les trois bibliothèques que nous avons présentées aujourd'hui sont uniques et apportent plus de possibilités pour les applications OpenResty. Enfin, un sujet interactif, avez-vous trouvé des bibliothèques intéressantes autour d'OpenResty ? Ou qu'avez-vous trouvé ou vous interrogez-vous sur les bibliothèques mentionnées aujourd'hui ? Vous êtes invités à envoyer cet article aux utilisateurs OpenResty autour de vous pour échanger et progresser ensemble.