Skip to content

SSE-Benachrichtigung

Der SSE (Server-Sent Events) Benachrichtigungsstrom ist der primäre Mechanismus, um einen Service-Client zu wecken, wenn neue Bestellungen oder Befehle verfügbar sind. Der Strom sendet nur leichte Benachrichtigungen. Er enthält niemals vollständige Bestell- oder Befehlspayloads. Wenn eine Benachrichtigung eingeht, sollte der Client sofort GET /orders/location für die tatsächlichen Daten abfragen.

Endpunkt

text
GET https://{host}/notifications/service/stream

Header:

HeaderErforderlichBeschreibung
api_keyJaAPI-Schlüssel des Service-Geräts

Query-Parameter:

ParameterTypStandardBeschreibung
orderNumberlong-1Zuletzt verarbeitete Bestellnummer für offset-basierte Wiederverbindung
commandNumberlong0Zuletzt verarbeitete Befehls-ID für offset-basierte Wiederverbindung

Antwort: text/event-stream (SSE-Stream mit SseEmitter)

Ereignisablauf

Nach der Verbindung sendet der Server Ereignisse in der folgenden Reihenfolge:

text
     Client                          Server
       |                               |
       |--- GET /notifications/service/stream --->|
       |                               |
       |<-- event: ready                 |
       |    data: {"subscriptionId": "...",|
       |           "locationName": "..."} |
       |                               |
       |<-- event: message              |  (bei neuen Bestellungen oder Befehlen)
       |    data: {"apiKey": "...",      |
       |           "licenseCode": "...", |
       |           "scope": "..."}      |
       |                               |
       |<-- event: keepalive            |  (alle 15 Sekunden)
       |    data: {}                   |

1. Ready-Ereignis

Bei erfolgreicher Verbindung sendet der Server sofort ein ready-Ereignis mit den Abonnement-Metadaten:

text
event: ready
data: {"subscriptionId":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","locationName":"Main Floor"}

Die subscriptionId identifiziert diese SSE-Verbindung eindeutig. Der locationName gibt an, welchen Standort der Client abonniert hat.

2. Message-Ereignisse

Wenn neue Bestellungen oder Befehle für den Standort verfügbar sind, sendet der Server ein message-Ereignis mit einer MessagesAvailableEvent-Payload:

text
event: message
data: {"apiKey":"abc123-def456","licenseCode":"LICENSE01","scope":"SESSION"}

Die MessagesAvailableEvent-Payload:

FeldTypBeschreibung
apiKeystringDer API-Schlüssel des Abonnements
licenseCodestringDer Lizenzcode des Mandanten
scopestringEntweder SESSION oder LICENSE, gibt den Broadcast-Bereich an

Dies ist ein Signal zum Abrufen. Es enthält keine Bestell- oder Befehlsdaten.

3. Keepalive-Ereignisse

Der Server sendet alle 15 Sekunden ein keepalive-Ereignis, um die Verbindung aufrechtzuerhalten:

text
event: keepalive
data: {}

Wenn 60 Sekunden lang kein Keepalive empfangen wird, sollte der Client annehmen, dass die Verbindung unterbrochen ist, und sich neu verbinden.

Verbindungs-Timeout

Der SSE-Stream hat ein Timeout von 60 Minuten Inaktivität. Wenn 60 Minuten lang keine Daten gesendet werden, schließt der Server die Verbindung. Das keepalive-Ereignis setzt dieses Timeout zurück, sodass die Verbindung in der Praxis offen bleibt, solange der Client Keepalives empfängt.

Wiederverbindung mit Offset

Stellen Sie bei der Wiederverbindung nach einer Unterbrechung die zuletzt verarbeitete Bestellnummer und Befehls-ID als Query-Parameter bereit. Dadurch wird sichergestellt, dass der Server die Benachrichtigung ab dem Punkt fortsetzt, an dem Sie aufgehört haben, und Sie keine Ereignisse verpassen:

bash
curl -i -H "api_key: abc123-def456-ghi789" \
  "https://{host}/notifications/service/stream?orderNumber=12345&commandNumber=42"
  • orderNumber — Die höchste bereits verarbeitete Bestell-number. Übergeben Sie -1 oder lassen Sie den Parameter weg, um keinen Offset zu verwenden (von Anfang an beginnen).
  • commandNumber — Die höchste bereits verarbeitete Befehls-ID. Übergeben Sie 0 oder lassen Sie den Parameter weg, um keinen Offset zu verwenden.

Der Server verwendet diese Offsets, um zu filtern, welche Bestellungen und Befehle Benachrichtigungen auslösen. Er verfolgt die gesendeten Offsets im Arbeitsspeicher für jedes Abonnement.

Fehlerantworten

Der SSE-Stream kann die folgenden HTTP-Statuscodes zurückgeben:

StatusBedeutung
401Nicht autorisiert — Ungültiger oder fehlender API-Schlüssel
503Dienst nicht verfügbar — Server vorübergehend nicht erreichbar. Client sollte mit exponentiellem Backoff wiederholen

Implementierungshinweise für Clients

Verwendung der EventSource API (Browser)

javascript
const apiKey = "abc123-def456-ghi789";
const lastOrderNumber = 12345; // Ihre zuletzt verarbeitete Bestellnummer
const lastCommandNumber = 42;  // Ihre zuletzt verarbeitete Befehls-ID

const eventSource = new EventSource(
  `https://{host}/notifications/service/stream` +
  `?api_key=${apiKey}` +
  `&orderNumber=${lastOrderNumber}` +
  `&commandNumber=${lastCommandNumber}`
);

eventSource.addEventListener("ready", (event) => {
  const data = JSON.parse(event.data);
  console.log("Verbunden. Abonnement:", data.subscriptionId);
  console.log("Standort:", data.locationName);
});

eventSource.addEventListener("message", (event) => {
  const data = JSON.parse(event.data);
  console.log("Neue Daten verfügbar für apiKey:", data.apiKey);
  // Sofort GET /orders/location aufrufen
  pollOrdersAndCommands();
});

eventSource.addEventListener("keepalive", () => {
  // Verbindung ist noch aktiv. Keine Aktion erforderlich.
});

eventSource.onerror = (err) => {
  console.error("SSE-Verbindungsfehler, Wiederverbindung läuft...", err);
  // EventSource verbindet automatisch neu, aber Sie sollten
  // exponentiellen Backoff implementieren und die Offset-Parameter aktualisieren
};

Verwendung einer SSE-Client-Bibliothek (Java/Kotlin)

kotlin
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.util.concurrent.atomic.AtomicLong

class SseClient(private val apiKey: String) {
    private val lastOrderNumber = AtomicLong(-1L)
    private val lastCommandNumber = AtomicLong(0L)
    private val client = HttpClient.newHttpClient()

    fun connect() {
        val url = "https://{host}/notifications/service/stream" +
            "?api_key=$apiKey" +
            "&orderNumber=${lastOrderNumber.get()}" +
            "&commandNumber=${lastCommandNumber.get()}"

        val request = HttpRequest.newBuilder()
            .uri(URI.create(url))
            .GET()
            .build()

        client.send(request, HttpResponse.BodyHandlers.ofLines()).use { response ->
            response.body().forEach { line ->
                when {
                    line.startsWith("event: ready") -> handleReady()
                    line.startsWith("event: message") -> handleMessage()
                    line.startsWith("data:") -> handleData(line.removePrefix("data:").trim())
                }
            }
        }
    }
}

Wiederverbindung mit exponentiellem Backoff

javascript
function connectWithBackoff(apiKey, maxRetries = 10) {
  let retryCount = 0;
  let lastOrderNumber = 0;
  let lastCommandNumber = 0;

  function connect() {
    const url = `https://{host}/notifications/service/stream` +
      `?api_key=${apiKey}` +
      `&orderNumber=${lastOrderNumber}` +
      `&commandNumber=${lastCommandNumber}`;

    const es = new EventSource(url);

    es.addEventListener("ready", (event) => {
      retryCount = 0;
      lastOrderNumber = 0;
      lastCommandNumber = 0;
    });

    es.addEventListener("message", () => {
      pollOrdersAndCommands();
    });

    es.onerror = () => {
      es.close();
      if (retryCount < maxRetries) {
        const delay = Math.min(1000 * Math.pow(2, retryCount), 60000);
        retryCount++;
        setTimeout(connect, delay);
      }
    };
  }

  connect();
}

Was der Client tun muss

  1. Die SSE-Verbindung mit dem API-Schlüssel öffnen.
  2. Bei ready die subscriptionId und locationName aufzeichnen.
  3. Bei message sofort GET /orders/location aufrufen.
  4. Die zuletzt verarbeiteten orderNumber und commandNumber für die Wiederverbindung im Auge behalten.
  5. Wenn die Verbindung abbricht, mit exponentiellem Backoff und den Offset-Parametern erneut verbinden.

Was der SSE-Stream NICHT tut

  • Der SSE-Stream enthält keine Bestelldaten oder Befehlspayloads. Er ist nur ein Wecksignal.
  • Der SSE-Stream bestätigt nicht, dass Sie Bestellungen verarbeitet haben. Die Verarbeitungsbestätigung erfolgt über die Statusaktualisierungs- und Befehlsantwort-Endpunkte.

Nächste Schritte