【问题标题】:How to ensure concurrency in Golang gorilla WebSocket packageGolang gorilla WebSocket包中如何保证并发
【发布时间】:2017-08-30 17:20:42
【问题描述】:

我研究过gorilla/websocket包的godoc。

在 Godoc 中明确指出

并发 连接支持一个并发读取器和一个并发写入器。

应用程序负责确保不超过一个 goroutine 同时调用写入方法(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
    }
}

来源: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 中调用,第二行在调用serveWs的 Goroutine 中执行

更重要的是,我应该如何确保不超过一个goroutine同时调用SetReadDeadlineReadMessageSetPongHandlerSetPingHandler

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

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

【问题讨论】:

  • 研究了一下包里面,如果我没看错的话,ping和pong回调函数实际上是在调用ReadMessage()的同一个GoRoutine中执行的。所以这个例子仍然保证了并发性。而SetPingHandler()和SetPongHandler()也是需要同步的,但是在例子中是在调用任何ReadMessage()之前调用的,这样就可以了。

标签: go websocket gorilla


【解决方案1】:

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

所有 Gorilla websocket 示例都使用这种方法,包括粘贴在问题中的示例。在示例中,所有对 read 方法的调用都来自 readPump 方法。 readPump 方法为单个 goroutine 上的连接调用一次。因此连接读取方法不会被并发调用。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-28
    • 1970-01-01
    • 2017-08-17
    • 2019-05-30
    • 1970-01-01
    • 2020-03-29
    相关资源
    最近更新 更多