Relay
Das Modul wippy/relay bietet eine WebSocket-Relay-Infrastruktur mit zweistufiger Hub-Architektur. Ein zentraler Hub verwaltet benutzerspezifische Hubs, die wiederum WebSocket-Client-Verbindungen verwalten und Nachrichten an Plugins routen.
Architektur
Central Hub
├── User Hub (alice)
│ ├── Plugin: session_
│ ├── Plugin: ai_
│ ├── WebSocket Client 1
│ └── WebSocket Client 2
├── User Hub (bob)
│ ├── Plugin: session_
│ └── WebSocket Client 1
└── ...
Der zentrale Hub läuft als Dienst. Wenn ein WebSocket-Client eine Verbindung herstellt, sucht oder erstellt der zentrale Hub einen User-Hub für diesen Benutzer. Der User-Hub verwaltet die Lebensdauer des Clients und routet Nachrichten basierend auf Befehls-Präfixen an Plugins.
Setup
Modul zum Projekt hinzufügen:
wippy add wippy/relay
wippy install
Abhängigkeit mit erforderlichen Parametern deklarieren:
version: "1.0"
namespace: app
entries:
- name: os_env
kind: env.storage.os
- name: processes
kind: process.host
lifecycle:
auto_start: true
- name: dep.relay
kind: ns.dependency
component: wippy/relay
version: "*"
parameters:
- name: application_host
value: app:processes
- name: env_storage
value: app:os_env
- name: user_security_scope
value: app.security:user_scope
Konfigurationsparameter
| Parameter | Erforderlich | Standard | Beschreibung |
|---|---|---|---|
application_host |
ja | — | Process Host für Relay-Prozesse |
env_storage |
nein | intern | Speicher für Umgebungsvariablen |
user_security_scope |
ja | — | Sicherheits-Scope für User-Hubs |
max_connections_per_user |
nein | 5 |
WebSocket-Verbindungen pro Benutzer |
queue_multiplier |
nein | 100 |
Nachrichten-Queue = Verbindungen × Multiplikator |
user_hub_inactivity_timeout |
nein | 7200s |
Idle-Zeit vor Hub-Bereinigung |
Client-Verbindungsablauf
- WebSocket-Client verbindet sich mit
user_idin den Metadaten - Zentraler Hub validiert die Verbindung und prüft die Limits pro Benutzer
- Zentraler Hub erstellt oder wiederverwendet einen User-Hub für den Benutzer
- User-Hub sendet eine
welcome-Nachricht an den Client:
{
"user_id": "alice",
"client_count": 1,
"plugins": [
{ "prefix": "session_", "process_id": "...", "status": "running" },
{ "prefix": "ai_", "process_id": "...", "status": "pending" }
]
}
Plugin-status ist einer von "not_started" (registriert, nie gestartet), "pending" (Start in Arbeit), "running", "failed" oder "stopped".
Nachrichten-Routing
Clients senden JSON-Nachrichten mit einem type-Feld. Der User-Hub vergleicht den Typ-Präfix mit registrierten Plugins und routet die Nachricht:
{ "type": "session_get_state", "data": { "key": "value" } }
Der Präfix session_ passt zum Session-Plugin. Der Hub entfernt den Präfix und sendet die Nachricht mit dem reduzierten Typ als Topic an den Plugin-Prozess:
-- process topic: "get_state"
-- payload:
{
conn_pid = client_pid,
type = "session_get_state", -- original full type preserved
data = { key = "value" },
request_id = "...",
session_id = "..."
}
Plugins antworten, indem sie Nachrichten zurück an conn_pid senden.
Plugins
Plugins sind process.lua-Einträge mit meta.type: relay.plugin:
entries:
- name: session_plugin
kind: process.lua
meta:
type: relay.plugin
command_prefix: session_
auto_start: true
source: file://session_plugin.lua
modules: [json, time, logger]
method: run
Plugin-Metadaten
| Feld | Typ | Beschreibung |
|---|---|---|
meta.type |
string | Muss relay.plugin sein |
meta.command_prefix |
string | Nachrichten-Typ-Präfix, den dieses Plugin verarbeitet |
meta.auto_start |
boolean | Starten, wenn der User-Hub initialisiert wird |
meta.default_host |
string | Process Host überschreiben |
Plugin-Lebenszyklus
Plugins werden vom User-Hub erzeugt. Beim Start empfängt das Plugin:
function run(args)
local user_id = args.user_id
local user_metadata = args.user_metadata
local user_hub_pid = args.user_hub_pid
local config = args.config
end
Das session_-Plugin empfängt Lebenszyklus-Nachrichten:
| Nachricht | Wann |
|---|---|
"resume" |
Erster Client verbindet sich mit dem User-Hub |
"shutdown" |
Letzter Client trennt sich vom User-Hub |
Plugins erhalten 1 automatischen Neustart bei einem Absturz. Nach einem zweiten Absturz wird das Plugin als "failed" markiert und nicht neu gestartet.
Plugin-Implementierung
Plugins empfangen Nachrichten in ihrer Prozess-Inbox. Jede Nachricht hat ein Topic (der entfernte Befehlspräfix) und ein Payload, das die ursprünglichen Nachrichtendaten zusammen mit conn_pid enthält, um Antworten an den Client zurückzusenden.
local json = require("json")
local function handle_message(topic, payload)
if topic == "get_state" then
process.send(payload.conn_pid, "ws.message", json.encode({
type = "session_state",
data = { status = "active" }
}))
end
end
local function run(args)
local user_id = args.user_id
local inbox = process.inbox()
local events = process.events()
while true do
local result = channel.select({
inbox:case_receive(),
events:case_receive()
})
if not result.ok then break end
if result.channel == inbox then
local msg = result.value
local topic = msg:topic()
local payload = msg:payload():data()
if topic == "resume" then
-- erster Client verbunden
elseif topic == "shutdown" then
-- letzter Client getrennt
else
handle_message(topic, payload)
end
elseif result.channel == events then
local event = result.value
if event.kind == process.event.CANCEL then
break
end
end
end
end
return { run = run }
Fehlerbehandlung
Der Relay sendet strukturierte Fehlermeldungen an Clients:
| Fehlercode | Beschreibung |
|---|---|
max_connections_reached |
Benutzer am Verbindungslimit |
missing_user_id |
Keine user_id in den Verbindungsmetadaten |
hub_creation_failed |
Erzeugung des User-Hubs fehlgeschlagen |
invalid_json |
Fehler beim Dekodieren der Nachricht |
unknown_command |
Nachricht ohne Type-Feld |
plugin_not_found |
Kein Plugin passt zum Befehlspräfix |
plugin_failed |
Plugin nicht verfügbar oder abgestürzt |
Hub-Lebenszyklus
User-Hub-Erstellung
User-Hubs werden bei Bedarf erstellt, sobald sich der erste Client für einen Benutzer verbindet. Der Hub wird mit dem Sicherheitsaktor und Scope des Benutzers erzeugt.
Garbage Collection
Der zentrale Hub prüft regelmäßig auf inaktive User-Hubs. Ein Hub ohne verbundene Clients für länger als user_hub_inactivity_timeout (Standard: 2 Stunden) wird mit einem 10-Sekunden-Cancel-Timeout sauber beendet.
Das GC-Prüfintervall wird automatisch abgeleitet: inactivity_timeout / 2.5.
Sicherheit
Der zentrale Hub läuft unter seiner eigenen Sicherheitsgruppe (wippy.relay.security:root) mit vollem Zugriff. Jeder User-Hub wird mit dem konfigurierten user_security_scope erzeugt, wodurch Operationen auf Benutzerebene isoliert werden.
Interne Topics
| Topic | Richtung | Beschreibung |
|---|---|---|
ws.join |
Client → Central/User Hub | Verbindungsanfrage |
ws.leave |
Client → Central/User Hub | Verbindungstrennung |
ws.message |
Client → User Hub | WebSocket-Nachricht |
ws.cancel |
Central → User Hub | Sauberes Herunterfahren |
ws.control |
Central → User Hub | Routing-Steuerung |
hub.activity_update |
User Hub → Central | Aktualisierung der Client-Anzahl |
Siehe auch
- WebSocket-Relay - Konfiguration des HTTP-WebSocket-Endpunkts
- Prozessmodell - Prozesslebenszyklus und Messaging
- Sicherheit - Sicherheitsaktoren und Scopes
- Framework-Übersicht - Verwendung des Framework-Moduls