Golang大猩猩WebSocket包如何保证并发
How to ensure concurrency in Golang gorilla WebSocket package
我研究了gorilla/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
}
}
这一行
c.ws.SetPongHandler(func(string) error {
c.ws.SetReadDeadline(time.Now().Add(pongWait)); return nil
})
和这一行
_, message, err := c.ws.ReadMessage()
好像没有同步,因为第一行是回调函数所以应该在包中创建的Goroutine中调用,第二行在调用serveWs
[=21=的Goroutine中执行]
更重要的是,如何保证不超过一个goroutine同时调用SetReadDeadline
、ReadMessage
、SetPongHandler
、SetPingHandler
?
我尝试使用互斥锁并在调用上述函数时将其锁定,然后再将其解锁,但很快我就意识到了一个问题。通常(也在示例中)在 for 循环中调用 ReadMessage
。但是,如果 Mutext 在 ReadMessage 之前被锁定,那么在收到下一条消息之前,其他读取函数都无法获取锁定并执行
有没有更好的方法来处理这个并发问题?提前致谢。
确保没有并发调用读取方法的最佳方法是从单个 goroutine 执行所有读取方法。
所有 Gorilla websocket 示例都使用这种方法,包括粘贴在问题中的示例。在该示例中,对读取方法的所有调用都来自 readPump
方法。 readPump
方法为单个 goroutine 上的连接调用一次。由此可见,connection read方法并不是并发调用的。
section of the documentation on control messages 表示应用程序必须读取连接才能处理控制消息。基于这个和 Gorilla 自己的示例,我认为可以安全地假设 ping、pong 和 close 处理程序将从应用程序的读取 goroutine 中调用,就像在当前实现中一样。如果文档可以更明确地说明这一点,那就太好了。也许提出问题?
我研究了gorilla/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
}
}
这一行
c.ws.SetPongHandler(func(string) error {
c.ws.SetReadDeadline(time.Now().Add(pongWait)); return nil
})
和这一行
_, message, err := c.ws.ReadMessage()
好像没有同步,因为第一行是回调函数所以应该在包中创建的Goroutine中调用,第二行在调用serveWs
[=21=的Goroutine中执行]
更重要的是,如何保证不超过一个goroutine同时调用SetReadDeadline
、ReadMessage
、SetPongHandler
、SetPingHandler
?
我尝试使用互斥锁并在调用上述函数时将其锁定,然后再将其解锁,但很快我就意识到了一个问题。通常(也在示例中)在 for 循环中调用 ReadMessage
。但是,如果 Mutext 在 ReadMessage 之前被锁定,那么在收到下一条消息之前,其他读取函数都无法获取锁定并执行
有没有更好的方法来处理这个并发问题?提前致谢。
确保没有并发调用读取方法的最佳方法是从单个 goroutine 执行所有读取方法。
所有 Gorilla websocket 示例都使用这种方法,包括粘贴在问题中的示例。在该示例中,对读取方法的所有调用都来自 readPump
方法。 readPump
方法为单个 goroutine 上的连接调用一次。由此可见,connection read方法并不是并发调用的。
section of the documentation on control messages 表示应用程序必须读取连接才能处理控制消息。基于这个和 Gorilla 自己的示例,我认为可以安全地假设 ping、pong 和 close 处理程序将从应用程序的读取 goroutine 中调用,就像在当前实现中一样。如果文档可以更明确地说明这一点,那就太好了。也许提出问题?