golang微服務框架kratos實現(xiàn)Socket.IO服務的方法
Socket.IO 是一個面向?qū)崟r web 應用的 實時通訊庫。它使得服務器和客戶端之間實時雙向的通信成為可能。底層使用EngineIO。SocketIO的的客戶端使用Engine.IO-Client,服務端使用Engine.IO實現(xiàn)。
Socket.IO 主要使用WebSocket協(xié)議。但是如果需要的話,Socket.IO 可以回退到幾種其它方法,例如Adobe Flash Sockets,JSONP拉取,或是傳統(tǒng)的AJAX拉取,并且在同時提供完全相同的接口。盡管它可以被用作WebSocket的包裝庫,它還是提供了許多其它功能,比如廣播至多個套接字,存儲與不同客戶有關的數(shù)據(jù),和異步IO操作。
Socket.IO如何工作
客戶端
EIO Socket 通過一個 XHR (XMLHttpRequest) 握手。前端發(fā)送一個 XHR,告訴服務端我要開始 XHR 長輪詢了。后端返回的數(shù)據(jù)里面包括一個 open 標志(數(shù)字 0 表示), 以及sid 和 upgrades 字段,ping時間間隔,ping超時時間。
{ "sid": "8b7ab1ae-fbcf-4d23-8192-3c14a2a90721", "upgrades": [ "websocket" ], "pingInterval": 10000, "pingTimeout": 60000 }
sid 是本次 EIO Socket 的會話 ID,因為一次 EIO Socket 包含了多個請求,而后端又會同時連接多個 EIO Socket,sid 的作用就相當于 SESSION ID。
另一個字段 upgrades,正常情況下是 ['websocket'],表示可以把連接方式從長輪詢升級到 WebSocket。
前端在發(fā)送第一個 XHR 的時候就開始了 XHR 長輪詢,這個時候如果有收發(fā)數(shù)據(jù)的需求,是通過長輪詢實現(xiàn)的。所謂長輪詢,是指前端發(fā)送一個 request,服務端會等到有數(shù)據(jù)需要返回時再 response. 前端收到 response 后馬上發(fā)送下一次 request。這樣就可以實現(xiàn)雙向通信。
前端收到握手的 upgrades 后,EIO 會檢測瀏覽器是否支持 WebSocket,如果支持,就會啟動一個 WebSocket 連接,然后通過這個 WebSocket 往服務器發(fā)一條內(nèi)容為 probe, 類型為 ping 的數(shù)據(jù)。如果這時服務器返回了內(nèi)容為 probe, 類型為 pong 的數(shù)據(jù),前端就會把前面建立的 HTTP 長輪詢停掉,后面只使用 WebSocket 通道進行收發(fā)數(shù)據(jù)
EIO Socket 生命周期內(nèi),會間隔一段時間 ping - pong 一次,用來測試網(wǎng)絡是否正常。
這是 WebSocket 幀的結構,綠色是發(fā)送,白色是接收。前面的數(shù)字是數(shù)據(jù)包類型,2 是 ping, 3 是 pong, 42是 message
服務端
服務端使用 ws 庫實現(xiàn) WebSocket 協(xié)議。http://socket.io 服務啟動時,會先啟動一個 ws 服務。http://socket.io 會監(jiān)聽 HTTP 服務器的 upgrade 和 request 事件。當 upgrade 事件觸發(fā)時,說明可能是 WebSocket 握手,先簡單校驗下,然后把請求交給 ws 服務進行處理,拿到 WebSocket 對象。當 request 事件觸發(fā)時,根據(jù) url 路徑判斷是不是 http://socket.io 的 XHR 請求,拿到 res 和 res 對象。這樣就可以正確接收和返回客戶端數(shù)據(jù)了,具體處理過程和前端部分是對應的。
Socket.IO的限制
與所有技術一樣,選擇正確的一種意味著明確您對產(chǎn)品未來的期望。與您自己創(chuàng)建Socket鏈接相比,SocketIO確實使許多事情變得更容易,但是除了上面提到的擴展問題之外,還有局限性和缺點。
首先是初始連接比WebSockets更長。這是因為它首先使用長輪詢和XHRPolling建立連接,然后升級到WebSocket(如果可用)。如果您不需要支持較舊的瀏覽器并且不擔心不支持WebSockets的客戶端環(huán)境,則可能不需要SocketIO的額外開銷。您可以通過指定僅與WebSockets連接來最大程度地減少這種影響。這將更改與WebSocket的初始連接,但是會關閉備選方案。
在代碼最小化的情況下,客戶端仍將需要下載61.2 KB的數(shù)據(jù)。
對于其他繁重的數(shù)據(jù)傳輸(例如,視頻流傳輸),Socket不是好的解決方案。如果要在此級別上支持數(shù)據(jù)交換,則更好的解決方案是webRTC或流數(shù)據(jù)傳輸服務商,Ably是其中之一。
Kratos服務端
首先安裝庫:
go get -u github.com/tx7do/kratos-transport/transport/socketio
然后實現(xiàn)一個簡單的服務端:
package main import ( ?? ?"context" ?? ?"os" ?? ?"os/signal" ?? ?"syscall" ?? ?"github.com/go-kratos/kratos/v2/log" ? ? transportSocketIO "github.com/tx7do/kratos-transport/transport/socketio" ?? ?socketio "github.com/googollee/go-socket.io" ) func main() { ?? ?interrupt := make(chan os.Signal, 1) ?? ?signal.Notify(interrupt, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) ?? ?ctx := context.Background() ?? ?srv := transportSocketIO.NewServer( ?? ??? ?transportSocketIO.WithAddress(":8000"), ?? ??? ?transportSocketIO.WithCodec("json"), ?? ??? ?transportSocketIO.WithPath("/socket.io/"), ?? ?) ?? ?srv.RegisterConnectHandler("/", func(s socketio.Conn) error { ?? ??? ?s.SetContext("") ?? ??? ?log.Info("connected:", s.ID()) ?? ??? ?return nil ?? ?}) ?? ?srv.RegisterEventHandler("/", "notice", func(s socketio.Conn, msg string) { ?? ??? ?log.Info("notice:", msg) ?? ??? ?s.Emit("reply", "have "+msg) ?? ?}) ?? ?srv.RegisterEventHandler("/chat", "msg", func(s socketio.Conn, msg string) string { ?? ??? ?s.SetContext(msg) ?? ??? ?return "recv " + msg ?? ?}) ?? ?srv.RegisterEventHandler("/", "bye", func(s socketio.Conn) string { ?? ??? ?last := s.Context().(string) ?? ??? ?s.Emit("bye", last) ?? ??? ?_ = s.Close() ?? ??? ?return last ?? ?}) ?? ?srv.RegisterErrorHandler("/", func(s socketio.Conn, e error) { ?? ??? ?log.Info("meet error:", e) ?? ?}) ?? ?srv.RegisterDisconnectHandler("/", func(s socketio.Conn, reason string) { ?? ??? ?log.Info("closed", reason) ?? ?}) ?? ?if err := srv.Start(ctx); err != nil { ?? ??? ?panic(err) ?? ?} ?? ?defer func() { ?? ??? ?if err := srv.Stop(ctx); err != nil { ?? ??? ??? ?t.Errorf("expected nil got %v", err) ?? ??? ?} ?? ?}() ?? ?<-interrupt }
JS客戶端
<!DOCTYPE html> <html lang="en"> <head> ? ? <title>Socket.IO chat</title> ? ? <meta charset='utf-8'> ? ? <meta http-equiv='X-UA-Compatible' content='IE=edge'> ? ? <meta name='viewport' content='width=device-width, initial-scale=1'> ? ? <style> ? ? ? ? * { ? ? ? ? ? ? margin: 0; ? ? ? ? ? ? padding: 0; ? ? ? ? ? ? box-sizing: border-box; ? ? ? ? } ? ? ? ? body { ? ? ? ? ? ? font: 13px Helvetica, Arial; ? ? ? ? } ? ? ? ? form { ? ? ? ? ? ? background: #000; ? ? ? ? ? ? padding: 3px; ? ? ? ? ? ? position: fixed; ? ? ? ? ? ? bottom: 0; ? ? ? ? ? ? width: 100%; ? ? ? ? } ? ? ? ? form input { ? ? ? ? ? ? border: 0; ? ? ? ? ? ? padding: 10px; ? ? ? ? ? ? width: 90%; ? ? ? ? ? ? margin-right: .5%; ? ? ? ? } ? ? ? ? form button { ? ? ? ? ? ? width: 9%; ? ? ? ? ? ? background: rgb(130, 224, 255); ? ? ? ? ? ? border: none; ? ? ? ? ? ? padding: 10px; ? ? ? ? } ? ? ? ? #messages { ? ? ? ? ? ? list-style-type: none; ? ? ? ? ? ? margin: 0; ? ? ? ? ? ? padding: 0; ? ? ? ? } ? ? ? ? #messages li { ? ? ? ? ? ? padding: 5px 10px; ? ? ? ? } ? ? ? ? #messages li:nth-child(odd) { ? ? ? ? ? ? background: #eee; ? ? ? ? } ? ? </style> </head> <body> <ul id="messages"></ul> <form action=""> ? ? <input id="m" autocomplete="off"/> ? ? <button>Send</button> </form> <!--<script src="https://cdn.socket.io/4.6.0/socket.io.min.js" integrity="sha384-c79GN5VsunZvi+Q/WObgk2in0CbZsHnjEqvFxC5DxHn9lTfNce2WW6h2pH6u/kF+" crossorigin="anonymous"></script>--> <script src="https://cdn.socket.io/socket.io-1.2.0.js"></script> <script src="https://code.jquery.com/jquery-1.11.1.js"></script> <script> ? ? var socket = io("ws://localhost:8000"); ? ? // var socket2 = io("http://localhost:8000/chat/"); ? ? socket.on('reply', function (msg) { ? ? ? ? $('#messages').append($('<li>').text(msg)); ? ? }); ? ? $('form').submit(function () { ? ? ? ? // socket2.emit('msg', $('#m').val(), function (data) { ? ? ? ? // ? ? $('#messages').append($('<li>').text('ACK CALLBACK: ' + data)); ? ? ? ? // }); ? ? ? ? socket.emit('notice', $('#m').val()); ? ? ? ? $('#m').val(''); ? ? ? ? return false; ? ? }); </script> </body> </html>
參考資料 (Reference)
go-socket.io
Socket.IO
什么是socketIO?
到此這篇關于golang微服務框架kratos實現(xiàn)Socket.IO服務的方法的文章就介紹到這了,更多相關golang Socket.IO內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
golang連接MongoDB數(shù)據(jù)庫及數(shù)據(jù)庫操作指南
MongoDB是Nosql中常用的一種數(shù)據(jù)庫,下面這篇文章主要給大家介紹了關于golang連接MongoDB數(shù)據(jù)庫及數(shù)據(jù)庫操作的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2022-09-09Go?Gin框架優(yōu)雅重啟和停止實現(xiàn)方法示例
Web應用程序中,有時需要重啟或停止服務器,無論是因為更新代碼還是進行例行維護,這時需要保證應用程序的可用性和數(shù)據(jù)的一致性,就需要優(yōu)雅地關閉和重啟應用程序,即不丟失正在處理的請求和不拒絕新的請求,本文將詳解如何在Go語言中使用Gin這個框架實現(xiàn)優(yōu)雅的重啟停止2024-01-01VS Code配置Go語言開發(fā)環(huán)境的詳細教程
這篇文章主要介紹了VS Code配置Go語言開發(fā)環(huán)境的詳細教程,本文通過實例代碼圖文相結合的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-05-05