2017-04-05 248 views
3

我研究過大猩猩/ 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

回答

1

確保沒有對讀取方法的併發調用的最佳方法是從一個goroutine執行所有讀取方法。

所有的大猩猩websocket示例都使用這種方法,包括粘貼在問題中的示例。在該示例中,所有對讀取方法的調用都來自readPump方法。對於單個goroutine上的連接,將調用readPump方法一次。由此可見,連接讀取方法並不是同時調用的。

section of the documentation on control messages表示應用程序必須讀取與過程控制消息的連接。基於這一點以及Gorilla自己的例子,我認爲可以肯定的是,ping,pong和close處理程序將從應用程序的讀入例程中調用,就像它在當前的實現中一樣。如果文檔可以更明確地說明這一點,那將會很好。也許提出問題?