背景
在公司的http server端代码中看到有这样的片段:
import "github.com/tmc/grpc-websocket-proxy/wsproxy"
wsProxy := wsproxy.WebsocketProxy(
coreMux,
wsproxy.WithLogger(logrus.StandardLogger()),
...
)
这里作了一个websocket的代理,将之前的grpc-gateway的流传输协议升级为ws
什么是websocket协议
HTTP协议是常用的应用层协议,是一种客户端请求服务端响应的模式,无法实现服务端想要主动向客户端推送的场景,如实现聊天室、push等。虽然可以通过轮询、长轮询、长连接去实现实时获取消息的能力,但是比如轮询需要客户端不断的发送请求,会有大量无效的请求;长轮询需要保持连接不断开一段时间,超时之后断开再重新连接,对于请求量不大的场景(如配置中心的动态更新,长轮询是不错的方式),但是假如连接数很多并且一直占用连接通道,对服务端资源是一种浪费。
websocket协议,最大的特点是服务端主动向客户端推送消息,客户端主动向服务端发送消息,是一种双向传输协议。
库使用
golang.org/x/net 相比于 github.com/gorilla ,缺少不少功能实现。
使用Upgrade将一个http请求升级为ws协议
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
func handler(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println(err)
return
}
... Use conn to send and receive messages.
}
建立好连接后,使用ReadMessage和WriteMessage进行消息的收发
for {
messageType, p, err := conn.ReadMessage()
if err != nil {
log.Println(err)
return
}
if err := conn.WriteMessage(messageType, p); err != nil {
log.Println(err)
return
}
}
(聊天室可以用这种方式实现,本质上是一个消息的广播,维护一个聊天室所有用户连接的map, 在ReadMessage后,对map中的所有连接发送msg)