Vor ein paar Monaten befand ich mich mitten in einem technischen Vorstellungsgespräch für eine mittlere Front-End-Position. Alles lief reibungslos, bis ich mit einer Frage konfrontiert wurde, die mich etwas überraschte.
„Stellen Sie sich vor, Sie brauchen eine Form der ständigen Kommunikation, um jede Sekunde etwas zu überprüfen, bis Sie etwas finden, das Sie brauchen.
Sie möchten beispielsweise ständig überprüfen, ob eine Zahlung erfolgreich war, wie bei einem E-Commerce-Setup. Wie würden Sie das angehen?“
Ich antwortete vorsichtig: „Ich denke, Sie könnten WebSockets implementieren, um das zu bewältigen.“
Der Interviewer lächelte. „Das ist eine gute Lösung, aber je nach Situation gibt es auch andere, wohl bessere Optionen.“
Und dann stürzten wir uns in ein Gespräch über verschiedene Ansätze zur Echtzeitkommunikation, darunter Long Polling, Short Polling, WebSockets und schließlich Server-Sent Events (SSE), was wohl die beste Wahl für einen unidirektionalen Datenstrom ist, wie in unserem Zahlungsbeispiel.
Wir haben auch die Wahl der richtigen Datenbank besprochen, um diese ständigen, aber leichten Anfragen zu verarbeiten, ohne Serverressourcen zu belasten. In diesem Zusammenhang entstand Redis, das für seine Einfachheit und Effizienz bei der Verwaltung dieser Art von Anfragen bekannt ist.
Dieses Gespräch ist mir im Gedächtnis geblieben. Mir wurde klar, dass WebSockets zwar viel Aufmerksamkeit erhalten, es aber eine breite Palette an Techniken gibt, die, wenn sie verstanden werden, die Art und Weise optimieren können, wie wir die Echtzeitkommunikation verwalten. Heute möchte ich diese vier Ansätze auf klare und ansprechende Weise aufschlüsseln, wann sie jeweils zum Einsatz kommen und welche Vor- und Nachteile sie haben. Am Ende werden Sie ein solides Verständnis dafür haben, warum Server-Sent Events (SSE) oft für die einseitige Echtzeitkommunikation von Vorteil sind.
Bevor ich anfange, möchte ich mich ganz herzlich bei Marcos bedanken, dem erfahrenen Senior Software Engineer, der dieses Gespräch geführt und mich Monate später dazu inspiriert hat, diesen Artikel zu schreiben, was ich sehr zu schätzen wusste, obwohl ich den Job nicht bekommen habe! :)
Bevor wir auf das SSE-Beispiel eingehen, wollen wir die vier Methoden aufschlüsseln, die wir in diesem Interview besprochen haben:
Kurzabfragen sind wahrscheinlich die einfachste Methode. Dabei wird in regelmäßigen Abständen eine Anfrage an den Server gestellt: „Haben Sie neue Daten?“ Der Server antwortet mit dem aktuellen Status – unabhängig davon, ob es etwas Neues gibt oder nicht.
Vorteil:
Nachteil:
Am besten geeignet für: Kleine, seltene Datenaktualisierungen, z. B. ein Börsenkurs, der etwa jede Minute aktualisiert wird.
Lange Umfragen gehen mit kurzen Umfragen noch einen Schritt weiter. Der Client fordert wiederholt Informationen vom Server an, aber anstatt sofort zu antworten, hält er die Verbindung aufrecht, bis neue Daten verfügbar sind. Sobald die Daten zurückgesendet wurden, öffnet der Client sofort eine neue Verbindung und wiederholt den Vorgang.
Oben:
Nachteil:
Am besten geeignet für: Situationen, in denen Echtzeitkommunikation erforderlich ist, WebSockets/SSE jedoch möglicherweise übertrieben sind (z. B. Chat-Anwendungen).
WebSockets sind eine modernere Lösung, die eine Vollduplex-Kommunikation zwischen Client und Server ermöglicht. Sobald eine Verbindung geöffnet ist, können beide Seiten ungehindert Daten senden, ohne die Verbindung erneut aufzubauen – was eine bidirektionale Kommunikation definiert.
Oben:
Nachteil:
Am besten geeignet für:Anwendungen, die eine ständige bidirektionale Kommunikation erfordern, wie Multiplayer-Spiele, Tools für die Zusammenarbeit, Chat-Anwendungen oder Echtzeitbenachrichtigungen.
Schließlich kommen wir zu Server-Sent Events (SSE), dem Helden unseres Zahlungsbeispiels. SSE erstellt eine unidirektionale Verbindung, bei der der Server Aktualisierungen an den Client sendet. Im Gegensatz zu WebSockets ist dies unidirektional – der Client sendet keine Daten zurück.
Oben:
Nachteil:
Am besten geeignet für: Echtzeit-Updates, bei denen der Kunde nur Daten wie Live-Ergebnisse, Benachrichtigungen und unser Beispiel für den Zahlungsstatus erhalten muss.
Lassen Sie uns zum Kern der Sache kommen. Ich habe eine einfache Next.js-App erstellt, um einen Echtzeit-Zahlungsprozess mithilfe von Server-Sent Events (SSE) zu simulieren. Es zeigt genau, wie Sie eine unidirektionale Kommunikation einrichten können, um den Status einer Zahlung zu überprüfen und den Benutzer zu benachrichtigen, wenn die Zahlung erfolgreich ist oder fehlschlägt.
Es ist ein bisschen schwierig, es für Next einzurichten, da es etwas anders funktioniert als einfaches JS, sodass Sie mir später danken können!
Hier ist das Setup:
In der folgenden Komponente haben wir eine einfache Benutzeroberfläche mit Schaltflächen zur Simulation verschiedener Arten von Transaktionen, die von einer tatsächlichen Gateway-API stammen würden (Pix, Stripe und eine fehlgeschlagene Kreditkartenzahlung). Diese Schaltflächen lösen Aktualisierungen des Zahlungsstatus in Echtzeit über SSE aus.
Hier passiert die SSE-Magie. Wenn eine Zahlung simuliert wird, öffnet der Client eine SSE-Verbindung, um auf Aktualisierungen vom Server zu warten. Es verwaltet verschiedene Status wie „Ausstehend“, „In Übertragung“, „Bezahlt“ und „Fehlgeschlagen“.
"use client"; import { useState } from "react"; import { PAYMENT_STATUSES } from "../utils/payment-statuses"; const paymentButtons = [ { id: "pix", label: "Simulate payment with Pix", bg: "bg-green-200", success: true, }, { id: "stripe", label: "Simulate payment with Stripe", bg: "bg-blue-200", success: true, }, { id: "credit", label: "Simulate failing payment", bg: "bg-red-200", success: false, }, ]; type transaction = { type: string; amount: number; success: boolean; }; const DOMAIN_URL = process.env.NEXT_PUBLIC_DOMAIN_URL; export function TransactionControl() { const [status, setStatus] = useState<string>(""); const [isProcessing, setIsProcessing] = useState<boolean>(false); async function handleTransaction({ type, amount, success }: transaction) { setIsProcessing(true); setStatus("Payment is in progress..."); const eventSource = new EventSource( `${DOMAIN_URL}/payment?type=${type}&amount=${amount}&success=${success}` ); eventSource.onmessage = (e) => { const data = JSON.parse(e.data); const { status } = data; console.log(data); switch (status) { case PAYMENT_STATUSES.PENDING: setStatus("Payment is in progress..."); break; case PAYMENT_STATUSES.IN_TRANSIT: setStatus("Payment is in transit..."); break; case PAYMENT_STATUSES.PAID: setIsProcessing(false); setStatus("Payment completed!"); eventSource.close(); break; case PAYMENT_STATUSES.CANCELED: setIsProcessing(false); setStatus("Payment failed!"); eventSource.close(); break; default: setStatus(""); setIsProcessing(false); eventSource.close(); break; } }; } return ( <div> <div className="flex flex-col gap-3"> {paymentButtons.map(({ id, label, bg, success }) => ( <button key={id} className={`${bg} text-background rounded-full font-medium py-2 px-4 disabled:brightness-50 disabled:opacity-50`} onClick={() => handleTransaction({ type: id, amount: 101, success }) } disabled={isProcessing} > {label} </button> ))} </div> {status && <div className="mt-4 text-lg font-medium">{status}</div>} </div> ); }
Serverseitig simulieren wir einen Zahlungsvorgang, indem wir periodische Statusaktualisierungen über SSE senden. Während die Transaktion voranschreitet, erhält der Kunde Updates darüber, ob die Zahlung noch aussteht, abgeschlossen wurde oder fehlgeschlagen ist.
import { NextRequest, NextResponse } from "next/server"; import { PAYMENT_STATUSES } from "../utils/payment-statuses"; export const runtime = "edge"; export const dynamic = "force-dynamic"; // eslint-disable-next-line @typescript-eslint/no-unused-vars export async function GET(req: NextRequest, res: NextResponse) { const { searchParams } = new URL(req.url as string); const type = searchParams.get("type") || null; const amount = parseFloat(searchParams.get("amount") || "0"); const success = searchParams.get("success") === "true"; if (!type || amount < 0) { return new Response(JSON.stringify({ error: "invalid transaction" }), { status: 400, headers: { "Content-Type": "application/json", }, }); } const responseStream = new TransformStream(); const writer = responseStream.writable.getWriter(); const encoder = new TextEncoder(); let closed = false; function sendStatus(status: string) { writer.write( encoder.encode(`data: ${JSON.stringify({ status, type, amount })}\n\n`) ); } // Payment gateway simulation async function processTransaction() { sendStatus(PAYMENT_STATUSES.PENDING); function simulateSuccess() { setTimeout(() => { if (!closed) { sendStatus(PAYMENT_STATUSES.IN_TRANSIT); } }, 3000); setTimeout(() => { if (!closed) { sendStatus(PAYMENT_STATUSES.PAID); // Close the stream and mark closed to prevent further writes writer.close(); closed = true; } }, 6000); } function simulateFailure() { setTimeout(() => { if (!closed) { sendStatus(PAYMENT_STATUSES.CANCELED); // Close the stream and mark closed to prevent further writes writer.close(); closed = true; } }, 3000); } if (success === false) { simulateFailure(); return; } simulateSuccess(); } await processTransaction(); // Return the SSE response return new Response(responseStream.readable, { headers: { "Access-Control-Allow-Origin": "*", Connection: "keep-alive", "X-Accel-Buffering": "no", "Content-Type": "text/event-stream; charset=utf-8", "Cache-Control": "no-cache, no-transform", "Content-Encoding": "none", }, }); }
Stellen Sie außerdem sicher, dass Sie die Datei .env.local mit diesem Inhalt hinzufügen:
NEXT_PUBLIC_DOMAIN_URL='http://localhost:3000'
Nachdem wir nun gesehen haben, wie man es implementiert, fragen Sie sich vielleicht: Warum sollte man dafür SSE statt WebSockets verwenden? Hier ist der Grund:
Diese Interviewfrage wurde zu einer unglaublichen Lernerfahrung und öffnete mir die Augen für die subtilen Unterschiede zwischen Long Polling, Short Polling, WebSockets und SSE. Jede Methode hat ihre Zeit und ihren Ort, und für die Optimierung der Echtzeitkommunikation ist es entscheidend zu verstehen, wann welche Methode anzuwenden ist.
SSE ist vielleicht nicht so glamourös wie WebSockets, aber wenn es um effiziente, einseitige Kommunikation geht, ist es das perfekte Tool für diesen Job – genau wie in unserem E-Commerce-Zahlungsbeispiel. Wenn Sie das nächste Mal etwas erstellen, das Echtzeitaktualisierungen erfordert, greifen Sie nicht standardmäßig nach WebSockets, sondern ziehen Sie SSE wegen seiner Einfachheit und Effizienz in Betracht.
Ich hoffe, dieser tiefe Einblick in Echtzeit-Kommunikationstechniken hält Sie fit für Ihr nächstes Projekt oder die knifflige Frage im Vorstellungsgespräch!
Next.js + TypeScript example repository: https://github.com/brinobruno/sse-next
Next.js + TypeScript example deployment: https://sse-next-one.vercel.app/
here are some authoritative sources and references you could explore for deeper insights:
MDN Web Docs: The WebSockets API
MDN Web Docs: Using Server Sent Events
Next.js: API Routes
I'll share my relevant socials in case you want to connect:
Github
LinkedIn
Portfolio
Das obige ist der detaillierte Inhalt vonEchtzeit-Webkommunikation: Long/Short Polling, WebSockets und SSE erklärt + Next.js-Code. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!