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

时间:2017-04-05 08:04:17

标签: go websocket gorilla

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

在Godoc中明确指出

  

并发   Connections支持一个并发读者和一个并发编写器。

     

应用程序负责确保不超过一个goroutine同时调用write方法(NextWriter,SetWriteDeadline,WriteMessage,WriteJSON,EnableWriteCompression,SetCompressionLevel),并且只有一个goroutine调用read方法(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
    }
}

来源: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同时调用SetReadDeadlineReadMessageSetPongHandlerSetPingHandler

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

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

1 个答案:

答案 0 :(得分:3)

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

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

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