main.go
import (
"github.com/gin-gonic/gin"
)
func main() {
...
// 连接ws会先发Get,正常返回101
r.GET("/ws", func(c *gin.Context) {
WsHandler(c.Writer, c.Request)
})
...
r.Run()
}
handler.go
websocket接入:
import "github.com/gorilla/websocket"
var wsupgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
HandshakeTimeout: 5 * time.Second,
// 取消ws跨域校验
CheckOrigin: func(r *http.Request) bool {
return true
},
}
// 处理ws请求
func WsHandler(w http.ResponseWriter, r *http.Request) {
var conn *websocket.Conn
var err error
conn, err = wsupgrader.Upgrade(w, r, nil)
if err != nil {
fmt.Println("Failed to set websocket upgrade: %+v", err)
return
}
// 必须死循环,gin通过协程调用该handler函数,一旦退出函数,ws会被主动销毁
for {
// recieve
t, reply, err := conn.ReadMessage()
if err != nil {
break
}
// todo:业务操作
}
}
分布式扩展,使用rds或者kafka等解耦合
当ws建立连接后,可轮询共享内存(rds)是否有新消息,若有新消息,则往ws写数据。
配合客户端重连机制,很好实现分布式长连接IM和PUSH扩展。
// 业务操作调用写rds
func SendMsg2Rds(uid int64, msg *WsMsg) {
// list 或者 pubsub 结构
}
// 业务操作调用读rds
func ReadMsgFromRds(uid int64) *WsMsg {
// list 或者 pubsub 结构
}
// 协程轮询rds:go RevAndSend
func RevAndSend(uid int64,ws *websocket.Conn) {
for {
// 若ws断开,则break
if msg := ReadMsgFromRds(uid); msg != nil {
// 向rds写数据
ws.WriteMessage(websocket.TextMessage, []byte(msg.ToJson()))
}
}
}
websocket 握手协议
客户端会发送Get请求,带有核心字段:
Upgrade: websocket
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
服务端会将101作为返回码返回,作为握手成功的标示