如何确保Golang大猩猩的并发WebSocket包
我研究过大猩猩/ websocket包的Godoc。如何确保Golang大猩猩的并发WebSocket包
在Godoc它明确提出
并发 连接支持一个并发的读写器和一个并行的作家。 (NextWriter,SetWriteDeadline,WriteMessage,WriteJSON,EnableWriteCompression,SetCompressionLevel)并且不超过一个goroutine调用读取方法(NextReader,SetReadDeadline,ReadMessage ,ReadJSON,SetPongHandler,SetPingHandler)。
Close和WriteControl方法可以与所有其他方法同时调用 方法。
然而,在由包提供的示例中的一个
func (c *Conn) readPump() {
defer func() {
hub.unregister <- c
c.ws.Close()
}()
c.ws.SetReadLimit(maxMessageSize)
c.ws.SetReadDeadline(time.Now().Add(pongWait))
c.ws.SetPongHandler(func(string) error {
c.ws.SetReadDeadline(time.Now().Add(pongWait)); return nil
})
for {
_, message, err := c.ws.ReadMessage()
if err != nil {
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
log.Printf("error: %v", err)
}
break
}
message = bytes.TrimSpace(bytes.Replace(message, newline, space, -1))
hub.broadcast <- message
}
}
此行
c.ws.SetPongHandler(func(string) error {
c.ws.SetReadDeadline(time.Now().Add(pongWait)); return nil
})
和这条线
_, message, err := c.ws.ReadMessage()
似乎是不同步的,因为第一行是一个回调函数,因此它应该在的goroutine调用在包中创建和第二行是在调用serveWs
更重要的是够程执行,应该怎么确保不超过一个goroutine同时调用SetReadDeadline
,ReadMessage
,SetPongHandler
,SetPingHandler
?
我尝试使用互斥锁并在每次调用上述函数时将其锁定,并在事后解锁,但很快我发现问题。通常(在这个例子中)ReadMessage
在for循环中被调用。但是,如果Mutext在ReadMessage之前被锁定,那么没有其他读取功能可以获取锁定并执行,直到接收到下一条消息为止
有没有更好的方法来处理这个并发问题?提前致谢。
确保没有对读取方法的并发调用的最佳方法是从一个goroutine执行所有读取方法。
所有的大猩猩websocket示例都使用这种方法,包括粘贴在问题中的示例。在该示例中,所有对读取方法的调用都来自readPump
方法。对于单个goroutine上的连接,将调用readPump
方法一次。由此可见,连接读取方法并不是同时调用的。
section of the documentation on control messages表示应用程序必须读取与过程控制消息的连接。基于这一点以及Gorilla自己的例子,我认为可以肯定的是,ping,pong和close处理程序将从应用程序的读入例程中调用,就像它在当前的实现中一样。如果文档可以更明确地说明这一点,那将会很好。也许提出问题?
在包内部学习之后,如果我没有误读它,ping和pong回调函数实际上是在与调用ReadMessage()相同的GoRoutine中执行的。所以这个例子仍然保证了并发性。而SetPingHandler()和SetPongHandler()它们也需要同步,但在这个例子中,它们在任何ReadMessage()被调用之前被调用,所以它没问题。 – user7509214