Comment construire un plugin Apache APISIX de 0 à 1 ?
Au cours des derniers mois, les utilisateurs de la communauté ont ajouté de nombreux plugins à Apache APISIX, enrichissant ainsi l'écosystème d'Apache APISIX. Du point de vue de l'utilisateur, l'émergence de plugins plus diversifiés est certainement une bonne chose, car ils répondent davantage aux attentes des utilisateurs pour une passerelle qui soit un processeur "tout-en-un" et "multifonction", tout en perfectionnant les performances élevées et la faible latence d'Apache APISIX.
Aucun des articles sur le blog d'Apache APISIX ne semble détailler le processus de développement des plugins. Alors, examinons ce processus du point de vue d'un développeur de plugin et voyons comment un plugin naît !
Cet article documente le processus de développement du plugin file-logger
par un ingénieur front-end sans expérience en back-end. Avant de plonger dans les détails du processus de mise en œuvre, nous présenterons brièvement la fonctionnalité de file-logger
.
Introduction du plugin file-logger
file-logger
prend en charge la génération de formats de journaux personnalisés en utilisant les métadonnées des plugins d'Apache APISIX. Les utilisateurs peuvent ajouter des données de requête et de réponse au format JSON dans des fichiers de journaux via le plugin file-logger
, ou pousser le flux de données de journaux vers un emplacement spécifié.
Imaginez ceci : lors de la surveillance du journal d'accès d'une route, nous ne nous intéressons pas seulement à la valeur de certaines données de requête et de réponse, mais nous souhaitons également écrire les données de journal dans un fichier spécifié. C'est là que le plugin file-logger
peut être utilisé pour aider à atteindre ces objectifs.
Nous pouvons utiliser file-logger
pour écrire les données de journal dans un fichier de journal spécifique afin de simplifier le processus de surveillance et de débogage.
Comment implémenter un plugin ?
Après avoir introduit les fonctionnalités de file-logger, vous aurez une meilleure compréhension de ce plugin. Voici une explication détaillée de la manière dont moi, en tant que développeur front-end sans expérience côté serveur, j'ai développé le plugin pour Apache APISIX et ajouté les tests correspondants.
Confirmer le nom et la priorité du plugin
Ouvrez le Guide de développement de plugins Apache APISIX et, par ordre de priorité, vous devez déterminer les deux choses suivantes :
- Déterminer la catégorie du plugin.
- Prioriser les plugins et mettre à jour le fichier
conf/config-default.yaml
.
Comme ce développement de file-logger
est un plugin de type journalisation, je me réfère au nom et à l'ordre des plugins de journalisation existants pour Apache APISIX et place file-logger
ici.
Après avoir consulté d'autres auteurs de plugins et des membres enthousiastes de la communauté, le nom file-logger
et la priorité 399
du plugin ont finalement été confirmés.
Notez que la priorité du plugin est liée à l'ordre d'exécution ; plus la valeur de la priorité est élevée, plus l'exécution est avancée. Et l'ordre des noms de plugins n'est pas lié à l'ordre d'exécution.
Créer un fichier de plugin minimal exécutable
Après avoir confirmé le nom et la priorité du plugin, vous pouvez créer notre fichier de code de plugin dans le répertoire apisix/plugins/
. Il y a deux points à noter ici :
- Si le fichier de code du plugin est créé directement dans le répertoire
apisix/plugins/
, il n'est pas nécessaire de modifier le fichierMakefile
. - Si votre plugin a son propre répertoire de code, vous devez mettre à jour le fichier
Makefile
, veuillez vous référer au Guide de développement de plugins Apache APISIX pour les étapes détaillées.
- Ici, nous créons le fichier
file-logger.lua
dans le répertoireapisix/plugins/
. - Ensuite, nous compléterons une version initialisée basée sur le
example-plugin
.
-- Introduire le module dont nous avons besoin dans l'en-tête
local log_util = require("apisix.utils.log-util")
local core = require("apisix.core")
local plugin = require("apisix.plugin")
local ngx = ngx
-- Déclarer le nom du plugin
local plugin_name = "file-logger"
-- Définir le format du schéma du plugin
local schema = {
type = "object",
properties = {
path = {
type = "string"
},
},
required = {"path"}
}
-- Schéma des métadonnées du plugin
local metadata_schema = {
type = "object",
properties = {
log_format = log_util.metadata_schema_log_format
}
}
local _M = {
version = 0.1,
priority = 399,
name = plugin_name,
schema = schema,
metadata_schema = metadata_schema
}
-- Vérifier si la configuration du plugin est correcte
function _M.check_schema(conf, schema_type)
if schema_type == core.schema.TYPE_METADATA then
return core.schema.check(metadata_schema, conf)
end
return core.schema.check(schema, conf)
end
-- Phase de journalisation
function _M.log(conf, ctx)
core.log.warn("conf: ", core.json.encode(conf))
core.log.warn("ctx: ", core.json.encode(ctx, true))
end
return _M
Une fois le fichier de plugin minimal exécutable prêt, les données de configuration du plugin et les données liées à la requête peuvent être sorties dans le fichier error.log
via core.log.warn(core.json.encode(conf))
et core.log.warn("ctx: ", core.json.encode(ctx, true))
.
Activer et tester le plugin
Voici quelques étapes pour les tests. Afin de tester si le plugin peut imprimer avec succès les données du plugin et les informations de données liées à la requête que nous avons configurées pour lui dans le fichier de journal d'erreurs, nous devons activer le plugin et créer une route de test.
-
Préparez un amont de test localement (l'amont de test utilisé dans cet article est
127.0.0.1:3030/api/hello
, que j'ai créé localement). -
Créez une route via la commande
curl
et activez notre nouveau plugin.curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' { "plugins": { "file-logger": { "path": "logs/file.log" } }, "upstream": { "type": "roundrobin", "nodes": { "127.0.0.1:3030": 1 } }, "uri": "/api/hello" }'
Vous verrez ensuite un code d'état
200
, indiquant que la route a été créée avec succès. -
Exécutez la commande
curl
pour envoyer une requête à la route afin de tester si le pluginfile-logger
a été démarré.curl -i http://127.0.0.1:9080/api/hello HTTP/1.1 200 OK ... hello, world
-
Dans le fichier
logs/error.log
, il y aura un enregistrement :Comme vous pouvez le voir, le
path: logs/file.log
que nous avons configuré pour le plugin dans le paramètre conf a été enregistré avec succès. À ce stade, nous avons créé avec succès un plugin minimal exécutable qui imprime les paramètresconf
etctx
dans la phase de journalisation.Ensuite, nous pouvons écrire la fonctionnalité principale pour le plugin
file-logger.lua
directement dans son fichier de code. Ici, nous pouvons directement exécuter la commandeapisix reload
pour recharger le dernier code du plugin sans redémarrer Apache APISIX.
Écrire la fonction principale pour le plugin file-logger
La fonction principale du plugin file-logger est d'écrire les données de journal. Après avoir demandé à d'autres membres de la communauté et vérifié les informations, j'ai appris à connaître la bibliothèque IO de Lua et confirmé que la logique de la fonction du plugin est à peu près les étapes suivantes.
-
Après chaque requête acceptée, sortir les données de journal dans le
path
configuré par le plugin.- Tout d'abord, obtenir la valeur de
path
dansfile-logger
viaconf
dans la phase de journalisation. - Ensuite, utiliser la bibliothèque IO de Lua pour créer, ouvrir, écrire, rafraîchir le cache et fermer le fichier.
- Tout d'abord, obtenir la valeur de
-
Gérer les erreurs telles que l'échec de l'ouverture du fichier, l'échec de la création du fichier, etc.
local function write_file_data(conf, log_message) local msg, err = core.json.encode(log_message) if err then return core.log.error("message json serialization failed, error info : ", err) end local file, err = io_open(conf.path, 'a+') if not file then core.log.error("failed to open file: ", conf.path, ", error info: ", err) else local ok, err = file:write(msg, '\n') if not ok then core.log.error("failed to write file: ", conf.path, ", error info: ", err) else file:flush() end file:close() end end
-
En me référant au code source du plugin
http-logger
, j'ai terminé la méthode de transmission des données de journal à l'écriture des données de journal et quelques jugements et traitements des métadonnées.function _M.log(conf, ctx) local metadata = plugin.plugin_metadata(plugin_name) local entry if metadata and metadata.value.log_format and core.table.nkeys(metadata.value.log_format) > 0 then entry = log_util.get_custom_format_log(ctx, metadata.value.log_format) else entry = log_util.get_full_log(ngx, conf) end write_file_data(conf, entry) end
Valider et ajouter des tests
Valider les enregistrements de journal
Comme le plugin file-logger
a été activé lors de la création de la route de test et que le chemin a été configuré comme logs/file.log
, nous pouvons simplement envoyer une requête à la route de test pour vérifier les résultats de la collecte de journaux à ce stade.
curl -i http://127.0.0.1:9080/api/hello
Dans le fichier logs/file.log correspondant, nous pouvons voir que chaque enregistrement est sauvegardé au format JSON. Après avoir formaté l'une des données, cela ressemble à ceci.
{
"server": {
"hostname": "....",
"version": "2.11.0"
},
"client_ip": "127.0.0.1",
"upstream": "127.0.0.1:3030",
"route_id": "1",
"start_time": 1641285122961,
"latency": 13.999938964844,
"response": {
"status": 200,
"size": 252,
"headers": {
"server": "APISIX/2.11.0",
"content-type": "application/json; charset=utf-8",
"date": "Tue, 04 Jan 2022 08:32:02 GMT",
"vary": "Accept-Encoding",
"content-length": "19",
"connection": "close",
"etag": "\"13-5j0ZZR0tI549fSRsYxl8c9vAU78\""
}
},
"service_id": "",
"request": {
"querystring": {},
"size": 87,
"method": "GET",
"headers": {
"host": "127.0.0.1:9080",
"accept": "*/*",
"user-agent": "curl/7.77.0"
},
"url": "http://127.0.0.1:9080/api/hello",
"uri": "/api/hello"
}
}
Cela conclut la vérification de la collecte des enregistrements de journal. Les résultats de la vérification indiquent que le plugin a été lancé avec succès et a renvoyé les données appropriées.
Ajouter plus de tests pour le plugin
Pour la partie add_block_preprocessor
du code, j'étais confus lorsque j'ai commencé à l'écrire car je n'avais aucune expérience préalable en Perl. Après avoir fait des recherches, j'ai compris la bonne façon de l'utiliser : si nous n'écrivons pas d'assertions request
et no_error_log
dans la section des données, alors l'assertion par défaut est la suivante.
--- request
GET /t
--- no_error_log
[error]
Après avoir pris en compte d'autres fichiers de test de journalisation, j'ai créé le fichier file-logger.t
dans le répertoire t/plugin/
.
Chaque fichier de test est divisé par **DATA**
en une section de préambule et une section de données. Comme il n'y a pas de classification claire des documents liés aux tests sur le site officiel, vous pouvez vous référer aux documents connexes à la fin de l'article pour plus de détails. Voici l'un des cas de test que j'ai complété après avoir consulté les documents pertinents.
use t::APISIX 'no_plan';
no_long_string();
no_root_location();
add_block_preprocessor(sub {
my ($block) = @_;
if (! $block->request) {
$block->set_value("request", "GET /t");
}
if (! $block->no_error_log && ! $block->error_log) {
$block->set_value("no_error_log", "[error]");
}
});
run_tests;
__DATA__
=== TEST 1: sanity
--- config
location /t {
content_by_lua_block {
local configs = {
-- full configuration
{
path = "file.log"
},
-- property "path" is required
{
path = nil
}
}
local plugin = require("apisix.plugins.file-logger")
for i = 1, #configs do
ok, err = plugin.check_schema(configs[i])
if err then
ngx.say(err)
else
ngx.say("done")
end
end
}
}
--- response_body_like
done
property "path" is required
Cela conclut la session d'ajout de tests pour le plugin.
Résumé
Ce qui précède est l'ensemble du processus de mise en œuvre d'un plugin Apache APISIX à partir de 0 en tant que débutant en back-end. J'ai rencontré beaucoup de pièges dans le processus de développement du plugin, mais heureusement, il y a de nombreux membres enthousiastes dans la communauté Apache APISIX pour m'aider à résoudre les problèmes, ce qui a rendu le développement et les tests du plugin file-logger relativement fluides tout au long. Si vous êtes intéressé par ce plugin, ou si vous souhaitez voir les détails du plugin, vous pouvez vous référer à la documentation officielle d'Apache APISIX.
Apache APISIX travaille également actuellement sur d'autres plugins pour prendre en charge davantage de services d'intégration, donc si vous êtes intéressé, n'hésitez pas à démarrer une discussion dans les Discussions GitHub, ou via la liste de diffusion.