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
GET https://{host}/notifications/service/streamHeader:
| Header | Erforderlich | Beschreibung |
|---|---|---|
api_key | Ja | API-Schlüssel des Service-Geräts |
Query-Parameter:
| Parameter | Typ | Standard | Beschreibung |
|---|---|---|---|
orderNumber | long | -1 | Zuletzt verarbeitete Bestellnummer für offset-basierte Wiederverbindung |
commandNumber | long | 0 | Zuletzt 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:
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:
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:
event: message
data: {"apiKey":"abc123-def456","licenseCode":"LICENSE01","scope":"SESSION"}Die MessagesAvailableEvent-Payload:
| Feld | Typ | Beschreibung |
|---|---|---|
apiKey | string | Der API-Schlüssel des Abonnements |
licenseCode | string | Der Lizenzcode des Mandanten |
scope | string | Entweder 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:
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:
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-1oder lassen Sie den Parameter weg, um keinen Offset zu verwenden (von Anfang an beginnen).commandNumber— Die höchste bereits verarbeitete Befehls-ID. Übergeben Sie0oder 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:
| Status | Bedeutung |
|---|---|
| 401 | Nicht autorisiert — Ungültiger oder fehlender API-Schlüssel |
| 503 | Dienst nicht verfügbar — Server vorübergehend nicht erreichbar. Client sollte mit exponentiellem Backoff wiederholen |
Implementierungshinweise für Clients
Verwendung der EventSource API (Browser)
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)
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
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
- Die SSE-Verbindung mit dem API-Schlüssel öffnen.
- Bei
readydiesubscriptionIdundlocationNameaufzeichnen. - Bei
messagesofortGET /orders/locationaufrufen. - Die zuletzt verarbeiteten
orderNumberundcommandNumberfür die Wiederverbindung im Auge behalten. - 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
- Polling — Bestellungen und Befehle bei Benachrichtigung abrufen
- Auf Befehle antworten — Auf abgerufene Befehle antworten