如何确保Golang gorilla WebSocket包中的并发性

我研究了大猩猩/ websocket包的Godoc.

在Godoc中明确指出

Concurrency
Connections support one concurrent reader and one concurrent writer.

Applications are responsible for ensuring that no more than one goroutine calls the write methods (NextWriter, SetWriteDeadline, WriteMessage, WriteJSON, EnableWriteCompression, SetCompressionLevel) concurrently and that no more than one goroutine calls the read methods (NextReader, SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler) concurrently.

The Close and WriteControl methods can be called concurrently with all other
methods.

但是,在包中提供的一个示例中

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
    }
}

资料来源:https://github.com/gorilla/websocket/blob/a68708917c6a4f06314ab4e52493cc61359c9d42/examples/chat/conn.go#L50

这条线

c.ws.SetPongHandler(func(string) error { 
    c.ws.SetReadDeadline(time.Now().Add(pongWait)); return nil
})

这条线

_, message, err := c.ws.ReadMessage()

似乎没有同步,因为第一行是一个回调函数,因此它应该在包中创建的Goroutine中调用,第二行在Goroutine中执行,调用serveWs

更重要的是,我应该如何确保不超过一个goroutine同时调用SetReadDeadline,ReadMessage,SetPongHandler,SetPingHandler?

我试图使用Mutex锁并在我调用上述函数时将其锁定,然后解锁它,但很快我就意识到了问题.通常(也在示例中)在for循环中调用ReadMessage.但是如果Mutext在ReadMessage之前被锁定,那么在收到下一条消息之前,没有其他的Read函数可以获取锁并执行

有没有更好的方法来处理这个并发问题?提前致谢.

最佳答案 确保没有对read方法的并发调用的最佳方法是从单个goroutine执行所有读取方法.

所有Gorilla websocket示例都使用此方法,包括粘贴在问题中的示例.在该示例中,对read方法的所有调用都来自readPump方法.对于单个goroutine上的连接,将调用readPump方法一次.因此,不会同时调用连接读取方法.

section of the documentation on control messages表示应用程序必须读取与过程控制消息的连接.基于这个和Gorilla自己的例子,我认为可以安全地假设ping,pong和close处理程序将从应用程序的读取goroutine中调用,就像在当前实现中一样.如果文档可以更明确地说明这一点会很好.也许提出问题?

点赞