معرفی Server-Sent Events (SSE)
رویدادهای ارسال شده توسط سرور یا SSE، یک فناوری فشار سرور است که به کلاینتها امکان میدهد به صورت خودکار بروزرسانیها را از سمت سرور از طریق یک اتصال HTTP دریافت کنند. این مشخص میکند که چگونه سرور پس از برقراری ارتباط اولیه با کلاینت، انتقال داده را راهاندازی میکند. این به طور معمول برای ارسال بهروزرسانیهای پیام یا جریانهای پیوسته اطلاعات به مرورگرهای کلاینت استفاده میشود، با هدف افزایش جریانهای محلی مرورگرها از طریق رابط برنامهنویسی جاوااسکریپت به نام EventSource. کلاینتها میتوانند از این API برای درخواست یک URL خاص برای دریافت جریانهای رویداد استفاده کنند. بهعنوان بخشی از HTML5، API EventSource توسط WHATWG استانداردسازی شدهاست. نوع رسانه برای SSE، text/event-stream است.
توجه: بزرگترین تفاوت بین SSE و Websocket این است که SSE یک فشار سرور به کلاینت یکطرفه است، درحالیکه Websocket یک فشار دوطرفه است. گاهی وقتها، زمانی که نیازهای تجاری به این تعقیب نمیبخشند، فشار پیام یکطرفه SSE کافی است. این مورد مشابه فناوریهای استفاده شده در گفتگوی هوش مصنوعی ChatGPT است.
مثال SSE فیبر
package main
import (
"bufio"
"fmt"
"log"
"time"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/valyala/fasthttp"
)
// شبیهسازی کلاینت HTML، اینجا یک مثال از دریافت پیامهای فشار داده شده از سمت پشتیبانی در JS است. در سناریوهای تجاری واقعی، کلاینت مرورگر این را پیادهسازی میکند.
var index = []بایت(`<!DOCTYPE html>
<html>
<body>
<h1>SSE Messages</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 = "Sorry, your browser does not support server-sent events...";
}
</script>
</body>
</html>
`)
func main() {
// نمونه Fiber
app := fiber.New()
// محدودیتهای CORS، اجازه دسترسی از هر دامنهای را میدهد
app.Use(cors.New(cors.Config{
AllowOrigins: "*",
AllowHeaders: "Cache-Control",
AllowCredentials: true,
}))
// دسترسی به مسیر /، ابتدا صفحه جلویی را برمیگرداند تا به فرمانی که فرمان مربوط به شروع فشار داده شده SSE از سمت پشتیبانی را بگیرد
app.Get("/", func(c *fiber.Ctx) error {
c.Response().Header.SetContentType(fiber.MIMETextHTMLCharsetUTF8)
return c.Status(fiber.StatusOK).Send(index)
})
// آدرس فشار داده شده SSE
app.Get("/sse", func(c *fiber.Ctx) error {
// تعیین هدرهای http sse، توجه به 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")
// شروع فشار داده شده به پیامها
c.Context().SetBodyStreamWriter(fasthttp.StreamWriter(func(w *bufio.Writer) {
fmt.Println("WRITER")
var i int
// شبیهسازی فشار داده شده پیوسته به کلاینت
for {
i++
msg := fmt.Sprintf("%d - the time is %v", i, time.Now())
// استفاده از نویسنده و تابع Fprintf برای فشار داده شده پیام به کلاینت، در سناریوهای تجاری واقعی، متن JSON برای پردازش آسان سمت جلویی ارسال میشود
fmt.Fprintf(w, "data: Message: %s\n\n", msg)
fmt.Println(msg)
// تخلیه دیتای خروجی به کلاینت
err := w.Flush()
if err != nil {
// تازهسازی صفحه در مرورگر و یک اتصال SSE جدید برقرار میکند، اما فقط آخرین پیوسته اصلی است، پس اتصالات مرده باید در اینجا بسته شوند.
fmt.Printf("Error while flushing: %v. Closing http connection.\n", err)
break
}
time.Sleep(2 * time.Second)
}
}))
return nil
})
// شروع سرور
log.Fatal(app.Listen(":3000"))
}