ENbext.dev
DocumentationExtensions

Traduction automatique depuis l’anglais. Consulter l’original.

Sur cette page

Plugins WASM

Le niveau WASM exécute des plugins en tant que modules WebAssembly dans le runtime wasmtime. Il offre des performances quasi natives avec une forte isolation de la mémoire et des limites de ressources déterministes via les budgets de carburant. Il s'agit d'une fonctionnalité Pro.

Comment ça marche#

  1. Vous écrivez votre plugin dans Rust (ou Go/C) et le compilez dans un module .wasm ciblant wasm32-wasi.
  2. bext charge le module via wasmtime au démarrage du serveur.
  3. Chaque appel de cycle de vie consomme du « carburant » – une abstraction de temps d’eau qui limite les calculs. Lorsque le carburant est épuisé, l'appel est terminé.
  4. Les fonctions hôtes du module d'importation "bext" fournissent le stockage, HTTP, les métriques et l'accès à la file d'attente.

Budgets de carburant#

Chaque hook de cycle de vie a un budget de carburant par défaut. Une unité de carburant correspond à peu près à une instruction WASM.

Crochet Carburant par défaut Durée approximative du mur
on_server_start 500,000,000 ~500 ms
on_request_complete 100,000,000 ~100 ms
on_cache_write 50,000,000 ~50 ms
on_cache_invalidate 50,000,000 ~50 ms
on_reload 200,000,000 ~200 ms
cleanup 100,000,000 ~100 ms

Remplacer les budgets dans bext.config.toml :

toml
[plugins.my-wasm-plugin]
sandbox = "wasm"
path = "./plugins/my-plugin.wasm"

[plugins.my-wasm-plugin.fuel]
on_server_start = 1_000_000_000
on_request_complete = 200_000_000

Importations de fonctions hôtes#

Les plugins WASM accèdent aux fonctionnalités de l'hôte via des fonctions importées du module "bext". Tous les types complexes sont transmis sous forme de chaînes codées JSON via des paires (ptr, len).

Fonctions hôtes disponibles#

Fonction Signature Description
bext.log (level_ptr, level_len, msg_ptr, msg_len) Journalisation structurée
bext.storage_get (key_ptr, key_len) -> (ptr, len) Lire à partir du magasin KV limité
bext.storage_set (key_ptr, key_len, val_ptr, val_len) -> i32 Écrivez au magasin KV
bext.storage_delete (key_ptr, key_len) -> i32 Supprimer l'entrée KV
bext.http_fetch (req_ptr, req_len) -> (ptr, len) Client HTTP (débit limité)
bext.get_config (key_ptr, key_len) -> (ptr, len) Lire la valeur de configuration du plugin
bext.emit_metric (name_ptr, name_len, value: f64, tags_ptr, tags_len) Émettre une métrique

La requête et la réponse http_fetch utilisent ce format JSON :

json
// Request
{ "url": "https://api.example.com/data", "method": "POST",
  "headers": [["Content-Type", "application/json"]], "body": "{}" }

// Response
{ "status": 200,
  "headers": [["content-type", "application/json"]],
  "body": "{\"result\": \"ok\"}" }

Autorisations du bac à sable#

toml
[plugins.my-wasm-plugin.permissions]
allowed_urls = ["https://api.example.com/*"]
storage_quota_kb = 1024
max_fetch_per_minute = 30
kv_namespaces = ["analytics"]
queue_names = ["events"]
  • Allowed_urls — modèles globaux pour HTTP sortant. Vide = pas d'accès au réseau.
  • storage_quota_kb — stockage KV maximum par plugin (par défaut : 1 024 Ko).
  • max_fetch_per_minute — limite de débit de compartiment de jetons pour les récupérations HTTP (par défaut : 30).
  • kv_namespaces — espaces de noms KV partagés auxquels le plugin peut accéder. Vide = limité au plugin uniquement.
  • queue_names — files d'attente de messages sur lesquelles le plugin peut publier ou consommer.

Construire un plugin Rust WASM#

1. Créez le projet#

bash
cargo new --lib my-analytics-plugin
cd my-analytics-plugin

2. Configurez Cargo.toml#

toml
[package]
name = "my-analytics-plugin"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
bext-plugin-api = "0.1"
serde = { version = "1", features = ["derive"] }
serde_json = "1"

3. Implémentez le trait LifecyclePlugin#

rust
use bext_plugin_api::lifecycle::LifecyclePlugin;
use serde::Deserialize;

#[derive(Deserialize)]
struct Config {
    endpoint: String,
    batch_size: usize,
}

struct AnalyticsPlugin {
    config: Option<Config>,
    buffer: Vec<String>,
}

impl LifecyclePlugin for AnalyticsPlugin {
    fn name(&self) -> &str {
        "my-analytics"
    }

    fn priority(&self) -> u32 {
        1000
    }

    fn on_server_start(&self, config_json: &str) -> Result<(), String> {
        let config: Config = serde_json::from_str(config_json)
            .map_err(|e| format!("parse config: {e}"))?;
        // Store config for later use
        bext::log("info", &format!("Analytics endpoint: {}", config.endpoint));
        Ok(())
    }

    fn on_request_complete(&self, event_json: &str) -> Result<(), String> {
        // Buffer events, flush when batch is full
        bext::storage_set("pending", event_json)?;
        bext::emit_metric("analytics_events", 1.0, &[("source", "wasm")]);
        Ok(())
    }

    fn cleanup(&self) -> Result<(), String> {
        bext::log("info", "Flushing remaining analytics events");
        Ok(())
    }
}

4. Compiler vers WASM#

bash
cargo build --target wasm32-wasi --release
cp target/wasm32-wasi/release/my_analytics_plugin.wasm ./plugin.wasm

5. Configurer dans bext#

toml
[plugins.my-analytics]
sandbox = "wasm"
path = "./plugins/my-analytics/plugin.wasm"

[plugins.my-analytics.config]
endpoint = "https://analytics.example.com/ingest"
batch_size = 100

[plugins.my-analytics.permissions]
allowed_urls = ["https://analytics.example.com/*"]
max_fetch_per_minute = 60

Débogage des plugins WASM#

Activez la journalisation WASI pour voir stdout/stderr depuis votre module WASM :

bash
BEXT_PLUGIN_LOG=debug bext dev

Les interruptions wasmtime (manque de carburant, violations de mémoire, instructions inaccessibles) sont enregistrées avec une trace de pile complète lorsque la journalisation du débogage est activée.

Pour profiler la consommation de carburant :

bash
bext plugin stats my-analytics

Cela rapporte la consommation de carburant par crochet, le nombre d'appels et le temps d'exécution moyen.

Plugins Go et C#

Tout langage compilé en wasm32-wasi fonctionne. Pour Go, utilisez TinyGo :

bash
tinygo build -o plugin.wasm -target=wasi ./main.go

Pour C, utilisez wasi-sdk :

bash
$WASI_SDK_PATH/bin/clang --sysroot=$WASI_SDK_PATH/share/wasi-sysroot \
  -o plugin.wasm plugin.c

La fonction hôte ABI est la même quelle que soit la langue source : chaînes JSON transmises via des paires (ptr, len).

Attention

L’épuisement du carburant interrompt l’appel du hook et consigne une erreur, mais ne provoque ni arrêt ni redémarrage de l’extension. L’appel suivant dispose d’un nouveau budget. Dimensionnez vos budgets avec prudence et utilisez bext plugin stats pour mesurer l’utilisation réelle avant de les augmenter en production.

Conseil

Si vous avez besoin de Python dans votre plugin WASM (par exemple pour exécuter un pipeline de données), consultez Plugins Python — Python s'exécute en tant que sous-mode Pyodide dans le même environnement d'exécution bext-plugin-wasm.

En rapport#

Modifier cette page ↗Besoin d’aide ? ↗
Rechercher dans la documentation

Saisissez un sujet, une commande ou une question.