Pengenalan tentang Server-Sent Events (SSE)

Server-Sent Events (SSE) adalah teknologi dorongan server yang memungkinkan klien menerima pembaruan secara otomatis dari server melalui koneksi HTTP. SSE menjelaskan bagaimana server memulai transfer data ke klien setelah koneksi awal klien terbentuk. Biasanya digunakan untuk mengirim pembaruan pesan atau aliran data kontinu ke klien browser, dengan tujuan meningkatkan aliran lintas browser lokal melalui API JavaScript yang disebut EventSource. Klien dapat menggunakan API ini untuk meminta URL tertentu guna menerima aliran acara. Sebagai bagian dari HTML5, API EventSource telah distandardisasi oleh WHATWG. Jenis media untuk SSE adalah text/event-stream.

Catatan: Perbedaan terbesar antara SSE dan Websocket adalah bahwa SSE adalah dorongan pesan satu arah dari server ke klien, sedangkan Websocket adalah dorongan pesan dua arah. Kadang-kadang, ketika persyaratan bisnis tidak terlalu kompleks, dorongan pesan satu arah SSE sudah memadai. Ini mirip dengan teknologi yang digunakan dalam percakapan AI ChatGPT.

Contoh SSE Fiber

package main

import (
	"bufio"
	"fmt"
	"log"
	"time"

	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/cors"
	"github.com/valyala/fasthttp"
)

// Menyimulasikan klien HTML, berikut contoh penerimaan pesan dorongan backend di JS; pada skenario bisnis sebenarnya, frontend akan mengimplementasikannya.
var index = []byte(`<!DOCTYPE html>
<html>
<body>

<h1>Pesan SSE</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 = "Maaf, browser Anda tidak mendukung server-sent events...";
}
</script>

</body>
</html>
`)

func main() {
	// Instance Fiber
	app := fiber.New()

	// Pembatasan CORS, mengizinkan akses dari domain manapun
app.Use(cors.New(cors.Config{
		AllowOrigins:     "*",
		AllowHeaders:     "Cache-Control",
		AllowCredentials: true,
	}))

	// Akses jalur /, kita pertama-tama mengembalikan halaman frontend, memungkinkan frontend untuk meminta backend untuk memulai dorongan pesan SSE
	app.Get("/", func(c *fiber.Ctx) error {
		c.Response().Header.SetContentType(fiber.MIMETextHTMLCharsetUTF8)

		return c.Status(fiber.StatusOK).Send(index)
	})

	// alamat dorongan pesan sse
	app.Get("/sse", func(c *fiber.Ctx) error {
		// Tetapkan header http sse, perhatikan 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")

		// Mulai mendorong pesan
		c.Context().SetBodyStreamWriter(fasthttp.StreamWriter(func(w *bufio.Writer) {
			fmt.Println("WRITER")
			var i int
			// Menyimulasikan terus-menerus mendorong pesan ke klien
			for {
				i++
				msg := fmt.Sprintf("%d - waktu sekarang %v", i, time.Now())
				// Gunakan writer dan fungsi Fprintf untuk mendorong pesan ke klien, pada skenario bisnis sebenarnya, teks JSON dapat dikirim untuk kemudahan pemrosesan frontend
				fmt.Fprintf(w, "data: Pesan: %s\n\n", msg)
				fmt.Println(msg)

				// Flush data output ke klien
				err := w.Flush()
				if err != nil {
					// Menyegarkan halaman dalam browser web akan membentuk kembali koneksi SSE baru, tetapi hanya yang terakhir yang aktif, jadi koneksi mati harus ditutup di sini.
					fmt.Printf("Kesalahan saat flushing: %v. Menutup koneksi http.\n", err)
					break
				}
				time.Sleep(2 * time.Second)
			}
		}))

		return nil
	})

	// Memulai server
	log.Fatal(app.Listen(":3000"))
}