Zustandslose Architektur
VCR führt mehrere Replikate jeder Anwendung aus und kann unter Last weiter skalieren. Replikate können im Leerlauf auch auf Null heruntergefahren werden. Dies hat wichtige Auswirkungen darauf, wie Sie den Status verwalten.
Warum In-Memory-Status unsicher ist
Alle im Speicher abgelegten Daten (globale Variablen, Objekte auf Modulebene, prozessinterne Caches) sind:
- Nur lokal für eine Replik
- Bei Neustart oder Skalierung auf Null verloren
- Unsichtbar für andere Replikate, die gleichzeitige Anfragen bearbeiten
Gehen Sie nie davon aus, dass zwei Anfragen auf dieselbe Replik treffen. Wenn dies heute der Fall ist, wird dies bei zunehmender Last nicht mehr der Fall sein.
Falsch:
// Replica-local — invisible to other replicas and lost on restart
const cache = {};
cache['userId'] = { name: 'Alice' };
let requestCount = 0;
requestCount++;
Richtig:
import { vcr } from '@vonage/vcr-sdk';
const state = vcr.getInstanceState();
await state.set('userId', { name: 'Alice' });
await state.increment('requestCount', 1);
Die Wahl des richtigen staatlichen Geltungsbereichs
| Umfang | Zugriffsmethode | Sichtbarkeit |
|---|---|---|
| Sitzung | new State(session) | Nur eine Sitzung |
| Instanz | vcr.getInstanceState() | Alle Replikate dieser bereitgestellten Instanz |
| Account | vcr.getAccountState() | Alle Applications im Vonage Account |
vcr.getInstanceState() ist der korrekte Standard für gemeinsam genutzte Zustände. Es handelt sich um einen praktischen Wrapper, der den Status auf die aktuelle Instanz-ID ausweitet, so dass alle Repliken desselben Deployments denselben Speicher nutzen.
vcr.getAccountState() ist für anwendungsübergreifende Daten gedacht, z. B. eine gemeinsame Blockliste oder Konfiguration, die mehrere VCR-Anwendungen lesen müssen.
Zustand der Sitzung (new State(session)) ist für Daten geeignet, die zu einer einzelnen Benutzerinteraktion gehören, wie z. B. Gesprächskontext oder Anrufstatus.
Häufig zu ersetzende In-Memory-Muster
| Muster | Ersatz |
|---|---|
const cache = {} auf Modulebene | vcr.getInstanceState() |
| Singleton-Objekt, das den Status der Anfrage enthält | vcr.getInstanceState() oder vcr.createSessionWithId(userId) |
global.something = ... | vcr.getInstanceState() |
| Zähler wird pro Anfrage inkrementiert | state.increment('counter', 1) |
Code-Beispiele
Node.js - gemeinsamer Zähler für alle Replikate:
import { vcr } from '@vonage/vcr-sdk';
const state = vcr.getInstanceState();
// Increment atomically — safe across replicas
await state.increment('requestCount', 1);
const count = await state.get('requestCount');
Node.js - Sitzungsstatus pro Benutzer:
import { vcr, State } from '@vonage/vcr-sdk';
app.post('/message', async (req, res) => {
// Each user gets an isolated state namespace
const session = vcr.createSessionWithId(req.body.userId);
const state = new State(session);
await state.set('lastSeen', new Date().toISOString());
res.sendStatus(200);
});
Python - gemeinsamer Status für alle Replikate:
from vonage_cloud_runtime.vcr import VCR
vcr = VCR()
state = vcr.getInstanceState()
await state.set('key', 'value')
value = await state.get('key')
Best Practices für den Sitzungsrahmen
Kritisch: Missbrauch von vcr.createSession() ist eine der häufigsten Ursachen für Fehler in VCR Applications. Das Verständnis von Session Scoping ist für die Erstellung von Anwendungen, die korrekt skalieren, unerlässlich.
Das Problem mit vcr.createSession() bei Global Scope
vcr.createSession() erzeugt eine zufällig, kurzlebig Sitzungs-ID jedes Mal, wenn sie aufgerufen wird. Wenn Sie es im Modul/Globalbereich aufrufen und die resultierende Sitzung mit new State(session):
- Jede Replik erstellt beim Start ihre eigene zufällige Sitzung
- Die von einer Replik geschriebenen Daten sind unsichtbar für alle anderen
- Bei einem Neustart oder einer Skalierung auf Null geht die Sitzungs-ID verloren und die Daten werden dauerhaft verwaist.
- Dies macht den Zweck der Nutzung des staatlichen Anbieters für gemeinsame Daten völlig zunichte
Anti-Muster - Tun Sie dies NICHT:
import { vcr, State } from '@vonage/vcr-sdk';
// WRONG: random session at global scope
const session = vcr.createSession();
const state = new State(session);
app.post('/message', async (req, res) => {
// This state is only accessible by this exact replica, using this exact session ID.
// Other replicas have their own random session and cannot see this data.
await state.set('messageCount', (await state.get('messageCount') || 0) + 1);
res.sendStatus(200);
});
Korrekte Muster
Für einen gemeinsamen Status für alle Replikate - Instanzstatus verwenden
import { vcr } from '@vonage/vcr-sdk';
// Shared across all replicas of this instance — no session needed
const state = vcr.getInstanceState();
app.post('/message', async (req, res) => {
await state.increment('messageCount', 1);
res.sendStatus(200);
});
Für den Status pro Gespräch/Benutzer - deterministische Sitzungs-IDs verwenden
Zuordnung von Sitzungen zum Anfragekontext mit Hilfe von IDs aus Rückrufen. Dadurch wird sichergestellt, dass jede Replik, die eine nachfolgende Anfrage für dieselbe Konversation/denselben Benutzer bearbeitet, auf denselben Status zugreifen kann.
import { vcr, State } from '@vonage/vcr-sdk';
// Voice: scope to the conversation UUID from the callback
app.post('/onCall', async (req, res) => {
const session = vcr.createSessionWithId(req.body.conversation_uuid);
const state = new State(session);
await state.set('step', 'greeting');
await state.set('startTime', Date.now());
res.json([{ action: 'talk', text: 'Welcome!' }]);
});
// SMS: scope to the sender's phone number
app.post('/onMessage', async (req, res) => {
const session = vcr.createSessionWithId(req.body.from);
const state = new State(session);
await state.increment('messageCount', 1);
await state.set('lastMessage', req.body.text);
res.sendStatus(200);
});
Die wichtigste Erkenntnis: die Sitzungs-ID muss deterministisch und aus dem Anfragekontext ableitbar sein. Gute Sitzungs-IDs sind beispielsweise:
| Quelle | Beispiel-ID | Anwendungsfall |
|---|---|---|
| Sprachrückruf | conversation_uuid | IVR-Status pro Anruf |
| SMS/MMS-Rückruf | Absender-Telefonnummer | Gesprächsverlauf pro Absender |
| Authentifizierter Benutzer | Benutzer-ID oder JWT-Betreff | Pro-Benutzer-Einstellungen |
| Benutzerdefinierte Korrelation | Bestell-ID, Ticket-ID | Status pro Arbeitsablauf |
Für die Registrierung von Abonnements - verwenden Sie Global Session
vcr.getGlobalSession() gibt eine deterministische Sitzung zurück, die von allen Replikaten gemeinsam genutzt wird. Verwenden Sie nur zur Registrierung von Provider-Abonnements (Sprache, Nachrichten), nicht zur Speicherung des Anwendungsstatus.
import { vcr, Voice, Messages } from '@vonage/vcr-sdk';
// Correct: global session for subscription registration
const globalSession = vcr.getGlobalSession();
const voice = new Voice(globalSession);
const messages = new Messages(globalSession);
await voice.onCall('onCall');
await messages.onMessage('onMessage',
{ type: 'sms', number: process.env.VONAGE_NUMBER },
{ type: 'sms', number: undefined }
);
Zusammenfassung
| Bedarf | Methode | Umfang |
|---|---|---|
| Gemeinsame Zähler, Caches, Konfiguration | vcr.getInstanceState() | Alle Repliken |
| Status pro Benutzer/Konversation | vcr.createSessionWithId(id) + new State(session) | Deterministisch, anforderungsspezifisch |
| Anwendungsübergreifende gemeinsame Daten | vcr.getAccountState() | Alle Anwendungen im Account |
| Anmeldung zum Abonnement | vcr.getGlobalSession() | Instanzweites Singleton |
| Niemals in globalem Umfang für den Staat | vcr.createSession() | Zufällig, unerreichbar für andere Replikate |