Heim >Web-Frontend >js-Tutorial >Echtzeit-Webkommunikation: Long/Short Polling, WebSockets und SSE erklärt + Next.js-Code

Echtzeit-Webkommunikation: Long/Short Polling, WebSockets und SSE erklärt + Next.js-Code

Linda Hamilton
Linda HamiltonOriginal
2024-09-23 22:30:32822Durchsuche

Real-Time Web Communication: Long/Short Polling, WebSockets, and SSE Explained + Next.js code

Hintergrundgeschichte: Die unerwartete Frage im Vorstellungsgespräch

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! :)


Die vier Methoden der Echtzeitkommunikation

Bevor wir auf das SSE-Beispiel eingehen, wollen wir die vier Methoden aufschlüsseln, die wir in diesem Interview besprochen haben:

1. Kurze Umfrage

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:

  • Einfach zu implementieren
  • Funktioniert mit herkömmlichen HTTP-Anfragen

Nachteil:

  • Es ist ressourcenintensiv. Sie stellen häufig Anfragen, auch wenn keine neuen Daten verfügbar sind.
  • Kann die Serverlast und den Netzwerkverkehr erhöhen, was bei häufigen Überprüfungen wie Zahlungsstatusaktualisierungen ineffizient wird.

Am besten geeignet für: Kleine, seltene Datenaktualisierungen, z. B. ein Börsenkurs, der etwa jede Minute aktualisiert wird.

2. Lange Abfrage

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:

  • Effizienter als kurze Abfragen, da der Server nur bei Bedarf antwortet – es ist wirklich schnell.
  • Kompatibel mit Browsern und HTTP/HTTPS-Protokollen.

Nachteil:

  • Erfordert immer noch das wiederholte Wiederöffnen von Verbindungen, was mit der Zeit zu Ineffizienz führt – teure Ressourcen.
  • Etwas komplexer als kurze Umfragen.

Am besten geeignet für: Situationen, in denen Echtzeitkommunikation erforderlich ist, WebSockets/SSE jedoch möglicherweise übertrieben sind (z. B. Chat-Anwendungen).

3. WebSockets

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:

  • Echte Echtzeitkommunikation mit minimaler Latenz.
  • Ideal für bidirektionale Kommunikation (z. B. Echtzeitspiele, Chat-Apps).

Nachteil:

  • Komplexer zu implementieren als Polling oder SSE.
  • WebSockets sind nicht immer ideal für einseitige Kommunikation oder weniger häufige Updates, da sie durch die Aufrechterhaltung offener Verbindungen Ressourcen verbrauchen können.
  • Möglicherweise ist eine Firewall-Konfiguration erforderlich.

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.

4. Vom Server gesendete Ereignisse (SSE)

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:

  • Ideal für einseitige Datenströme wie Newsfeeds, Börsenticker oder Zahlungsstatusaktualisierungen.
  • Leicht und einfacher zu implementieren als WebSockets.
  • Verwendet die vorhandene HTTP-Verbindung, daher gut unterstützt und Firewall-freundlich.

Nachteil:

  • Nicht für bidirektionale Kommunikation geeignet.
  • Einige Browser (insbesondere ältere IE-Versionen) unterstützen SSE nicht vollständig.

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.


SSE in Aktion: Zahlungsstatus in Echtzeit mit Next.js

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:

Frontend: Transaktionskontrollkomponente

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>
  );
}

Backend: SSE-Implementierung in Next.js

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'

Warum in diesem Fall SSE über WebSockets?

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:

  • Unidirektionale Kommunikation: In unserem Szenario muss der Kunde nur Updates über den Zahlungsstatus erhalten. Der Client muss nicht ständig Daten an den Server zurücksenden, daher passt die Einfachheit von SSE perfekt.
  • Leicht: Da SSE eine einzige HTTP-Verbindung zum Streamen von Updates verwendet, ist es ressourceneffizienter im Vergleich zu WebSockets, die eine Vollduplex-Kommunikation aufrechterhalten.
  • Firewall-freundlich: SSE ist in verschiedenen Netzwerkumgebungen einfacher zu verwenden, da es über HTTP ausgeführt wird, das normalerweise in Firewalls geöffnet ist, während bei WebSocket-Verbindungen manchmal Probleme auftreten können.
  • Browser-Unterstützung: Obwohl SSE nicht so weit verbreitet ist wie WebSockets, wird es von modernen Browsern unterstützt, was es für die meisten Anwendungsfälle, in denen unidirektionale Daten benötigt werden, zuverlässig macht.

Fazit: Kennen Sie Ihre Werkzeuge

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!


Let's get our hands dirty

Next.js + TypeScript example repository: https://github.com/brinobruno/sse-next
Next.js + TypeScript example deployment: https://sse-next-one.vercel.app/

References

here are some authoritative sources and references you could explore for deeper insights:

WebSockets And SSE Documentation:

MDN Web Docs: The WebSockets API
MDN Web Docs: Using Server Sent Events

Next API Routes

Next.js: API Routes


Let's connect

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!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn