如何确保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 
    } 
} 

来源: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同时调用SetReadDeadline,ReadMessage,SetPongHandler,SetPingHandler

我尝试使用互斥锁并在每次调用上述函数时将其锁定,并在事后解锁,但很快我发现问题。通常(在这个例子中)ReadMessage在for循环中被调用。但是,如果Mutext在ReadMessage之前被锁定,那么没有其他读取功能可以获取锁定并执行,直到接收到下一条消息为止

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

+0

在包内部学习之后,如果我没有误读它,ping和pong回调函数实际上是在与调用ReadMessage()相同的GoRoutine中执行的。所以这个例子仍然保证了并发性。而SetPingHandler()和SetPongHandler()它们也需要同步,但在这个例子中,它们在任何ReadMessage()被调用之前被调用,所以它没问题。 – user7509214

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

所有的大猩猩websocket示例都使用这种方法,包括粘贴在问题中的示例。在该示例中,所有对读取方法的调用都来自readPump方法。对于单个goroutine上的连接,将调用readPump方法一次。由此可见,连接读取方法并不是同时调用的。

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