---
title: "Qu'est-ce qui rend OpenResty si spécial ? - API7.ai"
description: "OpenResty est construit avec NGINX et LuaJIT. Commencez ici pour apprendre ses concepts de base et son fonctionnement."
url: "https://api7.ai/fr/learning-center/openresty/openresty-getting-started"
---

# Qu'est-ce qui rend OpenResty si spécial ?

API7.ai

October 14, 2022

[OpenResty (NGINX + Lua)](https://api7.ai/learning-center/openresty)

Dans les articles précédents, vous avez appris les deux pierres angulaires d'OpenResty : NGINX et LuaJIT, et je suis sûr que vous êtes prêt à commencer à apprendre les API fournies par OpenResty.

Mais ne soyez pas trop pressé. Avant de le faire, vous devez passer un peu plus de temps à vous familiariser avec les principes et les concepts de base d'OpenResty.

## Principes

![Diagram1](https://static.api7.ai/2022/10/13/634826e45b52e.png?imageMogr2/format/webp)

Les processus `Master` et `Worker` d'OpenResty contiennent tous deux une VM LuaJIT, qui est partagée par toutes les coroutines au sein du même processus, et dans laquelle le code Lua est exécuté.

Et à un moment donné, chaque processus `Worker` ne peut traiter que les requêtes d'un seul utilisateur, ce qui signifie qu'une seule coroutine est en cours d'exécution. Vous pourriez vous poser une question : Puisque NGINX peut supporter C10K (dizaines de milliers de concurrences), n'a-t-il pas besoin de traiter 10 000 requêtes simultanément ?

Bien sûr que non. NGINX utilise `epoll` pour piloter les événements afin de réduire l'attente et l'inactivité, de sorte que le plus de ressources CPU possible soient utilisées pour traiter les requêtes des utilisateurs. Après tout, l'ensemble ne parvient à une haute performance que lorsque les requêtes individuelles sont traitées suffisamment rapidement. Si un mode multi-thread est utilisé de sorte qu'une requête corresponde à un thread, alors avec C10K, les ressources peuvent facilement être épuisées.

Au niveau d'OpenResty, les coroutines de Lua fonctionnent en conjonction avec le mécanisme d'événements de NGINX. Si une opération d'I/O comme l'interrogation d'une base de données MySQL se produit dans le code Lua, elle appellera d'abord le `yield` de la coroutine Lua pour se suspendre, puis enregistrera un callback dans NGINX ; après que l'opération d'I/O soit terminée (ce qui pourrait aussi être un timeout ou une erreur), le callback `resume` de NGINX réveillera la coroutine Lua. Cela complète la coopération entre la concurrence Lua et les pilotes d'événements NGINX, évitant d'écrire des callbacks dans le code Lua.

Nous pouvons regarder le diagramme suivant, qui décrit l'ensemble du processus. `lua_yield` et `lua_resume` font tous deux partie de la `lua_CFunction` fournie par Lua.

![Diagram2](https://static.api7.ai/2022/10/13/634828bd1e4fe.png?imageMogr2/format/webp)

D'un autre côté, s'il n'y a pas d'opérations d'I/O ou de `sleep` dans le code Lua, comme toutes les opérations intensives de chiffrement et de déchiffrement, alors la VM LuaJIT sera occupée par la coroutine Lua jusqu'à ce que l'ensemble de la requête soit traité.

J'ai fourni un extrait de code source pour `ngx.sleep` ci-dessous pour vous aider à comprendre cela plus clairement. Ce code se trouve dans `ngx_http_lua_sleep.c`, que vous pouvez trouver dans le répertoire `src` du projet `lua-nginx-module`.

Dans `ngx_http_lua_sleep.c`, nous pouvons voir l'implémentation concrète de la fonction `sleep`. Vous devez d'abord enregistrer l'API Lua `ngx.sleep` avec la fonction C `ngx_http_lua_ngx_sleep`.

```
void ngx_http_lua_inject_sleep_api(lua_State *L)
{
     lua_pushcfunction(L, ngx_http_lua_ngx_sleep);
     lua_setfield(L, -2, "sleep");
}
```

Voici la fonction principale de `sleep`, et j'ai extrait seulement quelques lignes du code principal ici.

```
static int ngx_http_lua_ngx_sleep(lua_State *L)
{
    coctx->sleep.handler = ngx_http_lua_sleep_handler;
    ngx_add_timer(&coctx->sleep, (ngx_msec_t) delay);
    return lua_yield(L, 0);
}
```

Comme vous pouvez le voir :

- Ici, la fonction de rappel `ngx_http_lua_sleep_handler` est d'abord ajoutée.
- Ensuite, appelez `ngx_add_timer`, une interface fournie par NGINX, pour ajouter un minuteur à la boucle d'événements de NGINX.
- Enfin, utilisez `lua_yield` pour suspendre la concurrence Lua, en cédant le contrôle à la boucle d'événements de NGINX.

La fonction de rappel `ngx_http_lua_sleep_handler` est déclenchée lorsque l'opération de sommeil est terminée. Elle appelle `ngx_http_lua_sleep_resume` et finit par réveiller la coroutine Lua en utilisant `lua_resume`. Vous pouvez récupérer les détails de l'appel vous-même dans le code afin que je n'entre pas dans les détails ici.

`ngx.sleep` est juste l'exemple le plus simple, mais en le disséquant, vous pouvez voir les principes de base du module `lua-nginx-module`.

## Concepts de base

Après avoir analysé les principes, rafraîchissons notre mémoire et rappelons-nous les deux concepts importants des phases et du non-blocage dans OpenResty.

OpenResty, comme NGINX, a le concept de phases, et chaque phase a son propre rôle distinct :

- `set_by_lua`, qui est utilisé pour définir des variables.
- `rewrite_by_lua`, pour le transfert, la redirection, etc.
- `access_by_lua`, pour l'accès, les permissions, etc.
- `content_by_lua`, pour générer le contenu de retour.
- `header_filter_by_lua`, pour le traitement du filtre d'en-tête de réponse.
- `body_filter_by_lua`, pour le filtrage du corps de la réponse.
- `log_by_lua`, pour la journalisation.

Bien sûr, si la logique de votre code n'est pas trop complexe, il est possible de l'exécuter entièrement dans la phase `rewrite` ou `content`.

Cependant, notez que les API d'OpenResty ont des limites d'utilisation des phases. Chaque API a une liste de phrases dans lesquelles elle peut être utilisée, et vous obtiendrez une erreur si vous l'utilisez hors de portée. Cela est très différent des autres langages de développement.

À titre d'exemple, j'utiliserai `ngx.sleep`. D'après la documentation, je sais qu'il ne peut être utilisé que dans les contextes suivants et n'inclut pas la phase `log`.

```
context: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*_
```

Et si vous ne le savez pas, utilisez `sleep` dans une phase `log` qu'il ne supporte pas :

```
location / {
    log_by_lua_block {
        ngx.sleep(1)
     }
}
```

Dans le journal d'erreurs de NGINX, il y a une indication de niveau `error`.

```
[error] 62666#0: *6 failed to run log_by_lua*: log_by_lua(nginx.conf:14):2: API disabled in the context of log_by_lua*
stack traceback:
    [C]: in function 'sleep'
```

Donc, avant d'utiliser l'API, n'oubliez pas de consulter la documentation pour déterminer si elle peut être utilisée dans le contexte de votre code.

Après avoir revu le concept de phases, revoyons le non-blocage. Tout d'abord, clarifions que toutes les API fournies par OpenResty sont non bloquantes.

Je continuerai avec l'exigence de **sleep 1 seconde** comme exemple. Si vous voulez l'implémenter en Lua, vous devez faire ceci.

```
function sleep(s)
   local ntime = os.time() + s
   repeat until os.time() > ntime
end
```

Puisque Lua standard n'a pas de fonction `sleep`, j'utilise ici une boucle pour continuer à déterminer si le temps spécifié a été atteint. Cette implémentation est bloquante, et pendant la seconde où `sleep` est en cours d'exécution, Lua ne fait rien pendant que d'autres requêtes qui doivent être traitées attendent simplement.

Cependant, si nous passons à `ngx.sleep(1)`, selon le code source que nous avons analysé ci-dessus, OpenResty peut toujours traiter d'autres requêtes (comme `request B`) pendant cette seconde. Le contexte de la requête actuelle (appelons-la `request A`) sera sauvegardé et réveillé par le mécanisme d'événements de NGINX, puis reviendra à `request A`, de sorte que le CPU est toujours dans un état de travail naturel.

## Variables et cycle de vie

En plus de ces deux concepts importants, le cycle de vie des variables est également une zone facile à mal comprendre dans le développement d'OpenResty.

Comme je l'ai dit précédemment, dans OpenResty, je vous recommande de déclarer toutes les variables comme des variables locales et d'utiliser des outils comme `luacheck` et `lua-releng` pour détecter les variables globales. C'est la même chose pour les modules, comme ci-dessous.

```
local ngx_re = require "ngx.re"
```

Dans OpenResty, à l'exception des deux phases `init_by_lua` et `init_worker_by_lua`, une table isolée de variables globales est définie pour toutes les phases afin d'éviter de contaminer d'autres requêtes pendant le traitement. Même dans ces deux phases où vous pouvez définir des variables globales, vous devriez essayer d'éviter de le faire.

En règle générale, les problèmes qui tentent d'être résolus avec des variables globales devraient être mieux résolus avec des variables dans des modules et seront beaucoup plus clairs. Voici un exemple de variable dans un module.

```
local _M = {}

_M.color = {
      red = 1,
      blue = 2,
      green = 3
  }

  return _M
```

J'ai défini un module dans un fichier appelé `hello.lua`, qui contient la table `color`, puis j'ai ajouté la configuration suivante à `nginx.conf`.

```
location / {
    content_by_lua_block {
        local hello = require "hello"
        ngx.say(hello.color.green)
     }
}
```

Cette configuration exigera le module dans la phase `content` et imprimera la valeur de `green` comme corps de réponse HTTP.

Vous pourriez vous demander pourquoi la variable de module est si incroyable ?

Le module ne sera chargé qu'une seule fois dans le même processus `Worker` ; après cela, toutes les requêtes traitées par le `Worker` partageront les données du module. Nous disons que les données "globales" sont adaptées à l'encapsulation dans des modules parce que les `Worker` d'OpenResty sont entièrement isolés les uns des autres, donc chaque `Worker` charge le module indépendamment, et les données du module ne peuvent pas traverser les `Worker`.

Quant à la gestion des données qui doivent être partagées entre les `Worker`, je laisserai cela pour un chapitre ultérieur, donc vous n'avez pas à creuser ici.

Cependant, il y a une chose qui peut mal tourner ici : lors de l'accès aux variables de module, il est préférable de les garder en lecture seule et de ne pas essayer de les modifier, sinon vous obtiendrez une `race` dans le cas d'une haute concurrence, un bug qui ne peut pas être détecté par les tests unitaires, qui se produit occasionnellement en ligne et est difficile à localiser.

Par exemple, la valeur actuelle de la variable de module `green` est `3`, et vous faites une opération `plus 1` dans votre code, donc la valeur de `green` est-elle maintenant `4` ? Pas nécessairement ; cela pourrait être `4`, `5` ou `6` parce qu'OpenResty ne verrouille pas lors de l'écriture dans une variable de module. Ensuite, il y a compétition, et la valeur de la variable de module est mise à jour par plusieurs requêtes simultanément.

Ayant dit cela sur les variables globales, locales et de module, discutons des variables inter-phases.

Il y a des situations où nous avons besoin de variables qui traversent les phases et peuvent être lues et écrites. Des variables comme `$host`, `$scheme`, etc., qui nous sont familières dans NGINX, ne peuvent pas être créées dynamiquement même si elles satisfont à la condition inter-phases, et vous devez les définir dans le fichier de configuration avant de pouvoir les utiliser. Par exemple, si vous écrivez quelque chose comme ce qui suit.

```
location /foo {
      set $my_var ; # besoin de créer la variable $my_var d'abord
      content_by_lua_block {
          ngx.var.my_var = 123
      }
  }
```

OpenResty fournit `ngx.ctx` pour résoudre ce genre de problème. C'est une table Lua qui peut être utilisée pour stocker des données Lua basées sur la requête avec la même durée de vie que la requête actuelle. Regardons cet exemple de la documentation officielle.

```
location /test {
      rewrite_by_lua_block {
          ngx.ctx.foo = 76
      }
      access_by_lua_block {
          ngx.ctx.foo = ngx.ctx.foo + 3
      }
      content_by_lua_block {
          ngx.say(ngx.ctx.foo)
      }
  }
```

Vous pouvez voir que nous avons défini une variable `foo` qui est stockée dans `ngx.ctx`. Cette variable traverse les phases `rewrite`, `access` et `content` et finit par imprimer la valeur dans la phase `content`, qui est `79` comme nous l'attendions.

Bien sûr, `ngx.ctx` a ses limites.

Par exemple, les requêtes enfants créées avec `ngx.location.capture` auront leurs propres données `ngx.ctx` séparées, indépendantes des données `ngx.ctx` de la requête parent.

Ensuite, les redirections internes créées avec `ngx.exec` détruisent le `ngx.ctx` de la requête originale et le régénèrent avec un `ngx.ctx` vierge.

Ces deux limites ont des [exemples de code détaillés](https://github.com/openresty/lua-nginx-module#ngxctx) dans la documentation officielle, donc vous pouvez les vérifier vous-même si vous êtes intéressé.

## Résumé

Enfin, je dirai quelques mots de plus. Nous apprenons les principes d'OpenResty et quelques concepts importants, mais vous n'avez pas besoin de les mémoriser. Après tout, ils ont toujours un sens et prennent vie lorsqu'ils sont combinés avec des exigences et du code réels.

Je me demande comment vous le comprenez ? N'hésitez pas à laisser un commentaire et à discuter avec moi, et n'hésitez pas non plus à partager cet article avec vos collègues et amis. Nous communiquons ensemble, ensemble avec progrès.

Share article link

Share

Related Posts

[![Que se passe-t-il en cas de conflit entre les règles Lua et la configuration NGINX ?](https://static.api7.ai/2022/10/14/634974e117ebd.png)](https://api7.ai/learning-center/openresty/conflict-between-lua-rules-and-nginx)

## [Que se passe-t-il en cas de conflit entre les règles Lua et la configuration NGINX ?](https://api7.ai/learning-center/openresty/conflict-between-lua-rules-and-nginx)

[![Conseils pratiques : Identifier les concepts uniques et les pièges en Lua](https://static.api7.ai/2022/10/14/634974e2dd70a.png)](https://api7.ai/learning-center/openresty/lua-basic-concepts-and-pitfalls)

## [Conseils pratiques : Identifier les concepts uniques et les pièges en Lua](https://api7.ai/learning-center/openresty/lua-basic-concepts-and-pitfalls)

Table of Contents

- [Principes](#principes)
- [Concepts de base](#concepts-de-base)
- [Variables et cycle de vie](#variables-et-cycle-de-vie)
- [Résumé](#r%C3%A9sum%C3%A9)
