Einführung in servergesendete Ereignisse (SSE)
Servergesendete Ereignisse (SSE) sind eine Server-Push-Technologie, die es Clients ermöglicht, automatisch Updates vom Server über eine HTTP-Verbindung zu erhalten. Es beschreibt, wie der Server den Datentransfer an den Client initiiert, nachdem die initiale Client-Verbindung hergestellt wurde. Sie werden häufig verwendet, um Nachrichtenaktualisierungen oder kontinuierliche Datenströme an Browser-Clients zu senden, mit dem Ziel, lokale, plattformübergreifende Streams über das JavaScript-API namens EventSource zu verbessern. Clients können dieses API verwenden, um eine spezifische URL zur Empfang von Ereignisströmen anzufordern. Als Teil von HTML5 wurde das EventSource-API vom WHATWG standardisiert. Der Medientyp für SSE ist text/event-stream.
Hinweis: Der größte Unterschied zwischen SSE und Websocket besteht darin, dass SSE einen Einweg-Server-zu-Client-Naheichts-Push ist, während Websocket einen beidseitigen Naheichts-Push ist. Manchmal, wenn die Geschäftsanforderungen nicht so komplex sind, reicht der einseitige Naheichts-Push von SSE aus. Dies ähnelt der Technologie, die in ChatGPT AI-Gesprächen verwendet wird.
Fiber SSE-Beispiel
package main
import (
"bufio"
"fmt"
"log"
"time"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/valyala/fasthttp"
)
// Simuliere einen HTML-Client, hier ist ein Beispiel für den Frontend-Empfang von Backend-Push-Nachrichten in JS, in tatsächlichen Geschäftsszenarien wird dies vom Frontend implementiert.
var index = []byte(`<!DOCTYPE html>
<html>
<body>
<h1>SSE-Nachrichten</h1>
<div id="result"></div>
<script>
if(typeof(EventSource) !== "undefined") {
var source = new EventSource("http://127.0.0.1:3000/sse");
source.onmessage = function(event) {
document.getElementById("result").innerHTML += event.data + "<br>";
};
} else {
document.getElementById("result").innerHTML = "Entschuldigung, Ihr Browser unterstützt keine servergesendeten Ereignisse...";
}
</script>
</body>
</html>
`)
func main() {
// Fiber-Instanz
app := fiber.New()
// CORS-Beschränkungen, Zugriff von jeder Domain zulassen
app.Use(cors.New(cors.Config{
AllowOrigins: "*",
AllowHeaders: "Cache-Control",
AllowCredentials: true,
}))
// Zugriff auf den /-Pfad, wir liefern zuerst die Frontend-Seite, die es dem Frontend ermöglicht, vom Backend das Starten des SSE-Nachrichten-Push anzufordern
app.Get("/", func(c *fiber.Ctx) error {
c.Response().Header.SetContentType(fiber.MIMETextHTMLCharsetUTF8)
return c.Status(fiber.StatusOK).Send(index)
})
// SSE-Nachrichten-Push-Adresse
app.Get("/sse", func(c *fiber.Ctx) error {
// Setze SSE-HTTP-Header, achte auf Content-Type
c.Set("Content-Type", "text/event-stream")
c.Set("Cache-Control", "no-cache")
c.Set("Connection", "keep-alive")
c.Set("Transfer-Encoding", "chunked")
// Starte das Pushen von Nachrichten
c.Context().SetBodyStreamWriter(fasthttp.StreamWriter(func(w *bufio.Writer) {
fmt.Println("WRITER")
var i int
// Simuliere das kontinuierliche Pushen von Nachrichten an den Client
for {
i++
msg := fmt.Sprintf("%d - die Zeit ist %v", i, time.Now())
// Verwende den Writer und die Fprintf-Funktion, um Nachrichten an den Client zu pushen. In tatsächlichen Geschäftsszenarien kann JSON-Text für die Verarbeitung durch das Frontend gesendet werden.
fmt.Fprintf(w, "data: Nachricht: %s\n\n", msg)
fmt.Println(msg)
// Spüle die Ausgabedaten an den Client
err := w.Flush()
if err != nil {
// Das Aktualisieren der Seite im Webbrowser wird eine neue SSE-Verbindung herstellen, aber nur die letzte bleibt aktiv, so dass tote Verbindungen hier geschlossen werden müssen.
fmt.Printf("Fehler beim Spülen: %v. Schließe die HTTP-Verbindung.\n", err)
break
}
time.Sleep(2 * time.Second)
}
}))
return nil
})
// Starte den Server
log.Fatal(app.Listen(":3000"))
}