go語(yǔ)言如何使用gin庫(kù)實(shí)現(xiàn)SSE長(zhǎng)連接
前言
項(xiàng)目需求:在go項(xiàng)目中實(shí)現(xiàn)SSE長(zhǎng)連接,耗時(shí)操作完成后,后端主動(dòng)通知前端消息。
1.建立長(zhǎng)連接
最主要的操作是修改請(qǐng)求中的Content-Type類(lèi)型為"text/event-stream"。
需要注意幾點(diǎn),后端首先不能讓請(qǐng)求處理代碼跑完,如果跑完這個(gè)請(qǐng)求就會(huì)斷掉,保存的*gin.Context信息也就沒(méi)用了,因此要新建一個(gè)range chan維持處理狀態(tài)。
另外這里如果用for循環(huán)代替chan,會(huì)導(dǎo)致前端持續(xù)發(fā)送請(qǐng)求到后端。
import (
"encoding/json"
"fmt"
"github.com/gin-gonic/gin"
"github.com/labstack/gommon/log"
"net/http"
"strings"
"time"
)
var ifChannelsMapInit = false
var channelsMap = map[string]chan string{}
func initChannelsMap() {
channelsMap = make(map[string]chan string)
}
func AddChannel(userEmail string, traceId string) {
if !ifChannelsMapInit {
initChannelsMap()
ifChannelsMapInit = true
}
var newChannel = make(chan string)
channelsMap[userEmail+traceId] = newChannel
log.Infof("Build SSE connection for user = " + userEmail + ", trace id = " + traceId)
}
func BuildNotificationChannel(userEmail string, traceId string, c *gin.Context) {
AddChannel(userEmail, traceId)
c.Writer.Header().Set("Content-Type", "text/event-stream")
c.Writer.Header().Set("Cache-Control", "no-cache")
c.Writer.Header().Set("Connection", "keep-alive")
w := c.Writer
flusher, _ := w.(http.Flusher)
closeNotify := c.Request.Context().Done()
go func() {
<-closeNotify
delete(channelsMap, userEmail+traceId)
log.Infof("SSE close for user = " + userEmail + ", trace id = " + traceId)
return
}()
fmt.Fprintf(w, "data: %s\n\n", "--ping--")
flusher.Flush()
for msg := range channelsMap[userEmail+traceId] {
fmt.Fprintf(w, "data: %s\n\n", msg)
flusher.Flush()
}
}2.后端主動(dòng)通知前端消息
當(dāng)耗時(shí)操作處理完成后,調(diào)用該方法,前端會(huì)收到通知。
func SendNotification(userEmail string, messageBody string, actionType string) {
log.Infof("Send notification to user = " + userEmail)
var msg = dao.NotificationLog{}
msg.MessageBody = messageBody
msg.UserEmail = userEmail
msg.Type = actionType
msg.Status = UNREAD
msg.CreateTime = time.Now()
msg.Create()
msgBytes, _ := json.Marshal(msg)
for key := range channelsMap {
if strings.Contains(key, userEmail) {
channel := channelsMap[key]
channel <- string(msgBytes)
}
}
}3.調(diào)試
準(zhǔn)備兩個(gè)接口,分別是建立SSE和觸發(fā)耗時(shí)操作
GroupV1Rest.GET("/notification/socket-connection", controllers.SocketConnection)
GroupV1Rest.GET("/notification/export-excel", controllers.ExportExcel)打開(kāi)瀏覽器,進(jìn)入調(diào)試模式,在console頁(yè)輸入
e = new EventSource('/business/v1/notification/socket-connection');
e.onmessage = function(event) {
console.log(event.data);
};看到日志打印‘--ping--’,長(zhǎng)連接已建立

此時(shí)發(fā)送第二個(gè)請(qǐng)求,調(diào)試模式看到通知被chan處理

返回瀏覽器,可以看到已經(jīng)收到通知

4.關(guān)閉長(zhǎng)連接
前端關(guān)閉頁(yè)面后,自動(dòng)觸發(fā)監(jiān)聽(tīng)事件,后端清理連接信息

總結(jié)
到此這篇關(guān)于go語(yǔ)言如何使用gin庫(kù)實(shí)現(xiàn)SSE長(zhǎng)連接的文章就介紹到這了,更多相關(guān)go實(shí)現(xiàn)SSE長(zhǎng)連接內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語(yǔ)言中函數(shù)可變參數(shù)(Variadic Parameter)詳解
在Python中,在函數(shù)參數(shù)不確定數(shù)量的情況下,可以動(dòng)態(tài)在函數(shù)內(nèi)獲取參數(shù)。在Go語(yǔ)言中,也有類(lèi)似的實(shí)現(xiàn)方式,本文就來(lái)為大家詳細(xì)講解一下2022-07-07
通過(guò)手機(jī)案例理解Go設(shè)計(jì)模式之裝飾器模式的功能屬性
這篇文章主要為大家介紹了Go設(shè)計(jì)模式之裝飾器模式的功能屬性,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
golang中切片copy復(fù)制和等號(hào)復(fù)制的區(qū)別介紹
這篇文章主要介紹了golang中切片copy復(fù)制和等號(hào)復(fù)制的區(qū)別,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-04-04
golang如何通過(guò)viper讀取config.yaml文件
這篇文章主要介紹了golang通過(guò)viper讀取config.yaml文件,圍繞golang讀取config.yaml文件的相關(guān)資料展開(kāi)詳細(xì)內(nèi)容,需要的小伙伴可以參考一下2022-03-03
Go語(yǔ)言如何高效的進(jìn)行字符串拼接(6種方式對(duì)比分析)
本文主要介紹了Go語(yǔ)言如何高效的進(jìn)行字符串拼接(6種方式對(duì)比分析),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08
Golang實(shí)現(xiàn)Redis網(wǎng)絡(luò)協(xié)議實(shí)例探究
這篇文章主要為大家介紹了Golang實(shí)現(xiàn)Redis網(wǎng)絡(luò)協(xié)議實(shí)例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01

