ENbext.dev
DocumentationProtocoles

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

Sur cette page

Événements envoyés par le serveur

SSE est intégré au noyau bext — aucun indicateur de fonctionnalité n'est requis. bext fournit à la fois un générateur Response::sse de bas niveau pour les flux personnalisés et un SseChannelHub de haut niveau pour les chaînes pub/sub nommées.

Générateur de réponses#

Le moyen le plus simple d'envoyer SSE est le générateur Response::sse. Transmettez un flux d'événements asynchrone et un intervalle de reconnexion facultatif :

rust
use bext::Response;

async fn stream_updates(req: Request) -> Response {
    let stream = async_stream::stream! {
        for i in 0..10 {
            yield SseEvent::new()
                .id(i.to_string())
                .event("tick")
                .data(format!(r#"{{"count":{i}}}"#));
            tokio::time::sleep(Duration::from_secs(1)).await;
        }
    };

    Response::sse(stream)
        .reconnect_ms(3000)
        .build()
}

La réponse définit automatiquement Content-Type: text/event-stream, Cache-Control: no-cache et Connection: keep-alive.

Canaux nommés#

SseChannelHub permet à plusieurs éditeurs et abonnés de partager des chaînes nommées sans câbler manuellement les flux :

rust
use bext::sse::SseChannelHub;

// Publish from anywhere (handler, plugin, background task)
sse.publish("chat:room-42", SseEvent::new()
    .event("message")
    .data(r#"{"user":"alice","text":"hello"}"#));

// Subscribe in a handler
async fn listen(req: Request, sse: &SseChannelHub) -> Response {
    let stream = sse.subscribe("chat:room-42");
    Response::sse(stream).build()
}

Les chaînes sont créées paresseusement lors de la première publication ou de l'abonnement, et récupérées lorsque le dernier abonné se déconnecte.

Modèles de canaux#

Les noms de canaux prennent en charge les caractères génériques de style global pour la diffusion :

Modèle Matchs
chat:room-42 Canal exact uniquement.
chat:room-* Toutes les pièces sous chat.
notifications:* Tous les canaux de notification.

Contre-pression#

Les clients lents sont une réalité. bext gère la contre-pression sans bloquer le serveur ou les autres abonnés :

  1. Chaque abonné dispose d'un tampon limité (64 événements par défaut).
  2. Lorsque le tampon se remplit, les nouveaux événements sont supprimés pour cet abonné.
  3. Une métrique sse_events_dropped_total est émise par canal, par client.
  4. L'abonné reçoit un événement synthétique dropped avec le décompte de événements manqués afin que le client puisse demander une actualisation complète si nécessaire.

Configurez la taille du tampon par canal :

toml
[sse]
default_buffer_size = 64

[sse.channels.chat]
buffer_size = 256

Reconnexion du dernier ID d'événement#

Lorsqu'un client se reconnecte avec un en-tête Last-Event-ID, bext relit les événements manqués à partir d'un tampon en anneau en mémoire :

  • Taille de sonnerie par défaut : 1 000 événements par canal.
  • Si l'identifiant demandé est plus ancien que la sonnerie, le client reçoit un Événement replay_incomplete pour qu'il sache qu'il doit effectuer une récupération complète.
toml
[sse]
replay_ring_size = 1000

Le côté client est standard — le EventSource du navigateur envoie Last-Event-ID automatiquement lors de la reconnexion.

Exemple : réponses en streaming d'IA#

Les API LLM diffusent universellement les complétions via SSE. Voici un gestionnaire qui transmet un flux LLM en amont au navigateur :

rust
async fn ai_stream(req: Request) -> Response {
    let prompt = req.json::<PromptRequest>().await?;
    let upstream = llm_client.stream_completion(&prompt).await?;

    let stream = upstream.map(|chunk| {
        SseEvent::new()
            .event("token")
            .data(serde_json::to_string(&chunk).unwrap())
    });

    Response::sse(stream)
        .reconnect_ms(5000)
        .build()
}

Sur le client :

javascript
const source = new EventSource("/api/ai/stream?prompt=...");
source.addEventListener("token", (e) => {
  const chunk = JSON.parse(e.data);
  appendToOutput(chunk.text);
});

Métrique#

bext expose des métriques spécifiques à SSE :

Métrique Description
sse_active_connections Connexions SSE actuellement ouvertes.
sse_events_sent_total Événements envoyés sur tous les canaux.
sse_events_dropped_total Les événements ont été abandonnés en raison de la contre-pression.
sse_channels_active Chaînes avec au moins un sous.

Compression#

Les réponses SSE sont automatiquement compressées avec gzip ou brotli lorsque le client envoie Accept-Encoding. Étant donné que SSE est une connexion de longue durée, bext vide le compresseur après chaque événement pour éviter les retards de mise en mémoire tampon.

Attention

Le tampon de réponses d'acheminement /api/* de bext jusqu'à son achèvement avant la livraison — les flux SSE envoyés à partir d'un gestionnaire /api/ ne seront PAS vidés en direct. Placez votre gestionnaire SSE en dehors du préfixe /api/ (ou sous une route de page de niveau supérieur) pour obtenir un véritable streaming. Voir le Le piège de la mise en mémoire tampon SSE pour plus de détails.

Conseil

Si un client lent remplit son tampon de 64 événements, il reçoit un message synthétique Événement dropped plutôt qu’une déconnexion matérielle. Votre client doit écouter cet événement et déclencher une actualisation complète, sinon il manquera silencieusement les mises à jour.

En rapport#

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

Saisissez un sujet, une commande ou une question.