go語言如何使用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類型為"text/event-stream"。
需要注意幾點(diǎn),后端首先不能讓請(qǐng)求處理代碼跑完,如果跑完這個(gè)請(qǐng)求就會(huì)斷掉,保存的*gin.Context信息也就沒用了,因此要新建一個(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)
打開瀏覽器,進(jìn)入調(diào)試模式,在console頁輸入
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)閉頁面后,自動(dòng)觸發(fā)監(jiān)聽事件,后端清理連接信息
總結(jié)
到此這篇關(guān)于go語言如何使用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)文章
Apache?IoTDB開發(fā)系統(tǒng)之Go原生接口方法
這篇文章主要為大家介紹了?Apache?IoTDB開發(fā)系統(tǒng)之Go原生接口方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09golang實(shí)現(xiàn)簡(jiǎn)單的udp協(xié)議服務(wù)端與客戶端示例
這篇文章主要介紹了golang實(shí)現(xiàn)簡(jiǎn)單的udp協(xié)議服務(wù)端與客戶端,結(jié)合實(shí)例形式分析了基于UDP協(xié)議的數(shù)據(jù)傳輸相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2016-07-07