go websokcet实战(1)
1.背景
最近做HIDS项目中遇到一个需求,需要server端能随时主动给client端推送策略消息,而不需要等待client端的心跳消息上报。系统原有通信方式使用的http,server不具备这种能力,所有需要将系统的通信方式更改为websocket的方式。
使用的过程中查找了一些资料,但是我们这边的有一个需求点是,希望server端每间隔一定时间(比如2小时)主动断开连接在这个server上的所有连接,让client端重新连接。原因是server端是分布式的,有10个实例,如果不周期性的进行断开重连,一旦一个server崩溃重启后,就没有client能连接这个server实例了。
2.什么是websokcet协议
WebSocket是基于TCP的应用层协议,用于在C/S架构的应用中实现双向通信,关于WebSocket协议的详细规范和定义参见rfc6455。虽然WebSocket协议在建立连接时会使用HTTP协议,但这并不意味着WebSocket协议是基于HTTP协议实现的。详见https://www.cnblogs.com/nuccch/p/10947256.html
websocket协议的使用场景:
1.有双向通信的需求
2.通信频繁,提升通信效率的需求
3.使用库
go语言自带一个websocket相关的库 golang.org/x/net,但是查资料,发现说github上的github.com/gorilla/websocket库比自带的更好用,性能更好,就果断选择了github.com/gorilla/websocket 库进行研究。
3.1客户端发起连接创建
客户端通过Dialer函数发起连接的创建,然后通过创建的链接对象来发送消息。
url := "要连接的服务端地址"
dialer := &websocket.Dialer{
Proxy: http.ProxyFromEnvironment,
HandshakeTimeout: 120 * time.Second,
}
client, _, err := dialer.Dial(url, nil)
不断的从chan中取出消息,然后发送消息给server端。
reportMsgChan := make(chan []byte, 10)
for {
select {
case msg := <-reportMsgChan:
err := client.Conn.WriteMessage(websocket.BinaryMessage, msg)
if err != nil {
reportMsgChan <- msg
client.Conn.Close()
goto DONE
}
}
DONE:
return
3.2服务端接收连接
服务端调用Upgrader.Upgrade 函数将来自客户端的http请求升级为长连接,获得一个连接对象*Conn:
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
}
for { // 使用conn接收来自客户端的消息
messageType, p, err := conn.ReadMessage()
if err != nil {
log.Println(err)
return
}
// 解析消息跑,并处理
......
}
}
4.其他注意事项
- 如果客户端有多个协程同时调用Write相关方法往server端发消息不允许的,会发生异常,解决的办法是在调用write相关的方法前先获取全局锁,以保证同一时刻只有一个协程调用次方法;另外一个解决办法就是上面展示的,使用chan,将需要发送的消息存入chan,然后由这个专门的发送数据的协程从chan中取出消息,来发送。
- 如果服务端主动关闭连接,为了不引起客户端的异常,可以提前给客户端发送一个CloseMessage类型,告诉客户端”我要关闭连接了“。
- conn的相关Write和Read系列方法,都是阻塞式的,为了提高系统的可用性,可设置接口的超时时间限制,避免因网络的原因造成接口阻塞,一直不能发送消息或者写消息,系统却无任何感知。(调用SetReadDeadline()或SetWriteDeadline()方法,当没有业务数据需要发送时,可以借助发送PingMessage或PongMessage)
【参考】
https://www.cnblogs.com/nuccch/p/10947256.html
https://godoc.org/github.com/gorilla/websocket