Procházet zdrojové kódy

Improve chat example

- Discuss concurrency and message coalescing in the README.
- Add comments to client.go explaining how concurrency requirements are
  met.
- Prevent developers from calling the Client.write method from outside
  of the writePump goroutine by removing the method. The code is now
  inlined in Client.writPump.
Gary Burd před 10 roky
rodič
revize
6257d10a8b
2 změnil soubory, kde provedl 23 přidání a 9 odebrání
  1. 11 0
      examples/chat/README.md
  2. 12 9
      examples/chat/client.go

+ 11 - 0
examples/chat/README.md

@@ -72,6 +72,17 @@ there's an error writing to the websocket connection.
 Finally, the HTTP handler calls the client's `readPump` method. This method
 transfers inbound messages from the websocket to the hub.
 
+WebSocket connections [support one concurrent reader and one concurrent
+writer](https://godoc.org/github.com/gorilla/websocket#hdr-Concurrency). The
+application ensures that these concurrency requirements are met by executing
+all reads from the `readPump` goroutine and all writes from the `writePump`
+goroutine.
+
+To improve efficiency under high load, the `writePump` function coalesces
+pending chat messages in the `send` channel to a single WebSocket message. This
+reduces the number of system calls and the amount of data sent over the
+network.
+
 ## Frontend
 
 The frontend code is in [home.html](https://github.com/gorilla/websocket/blob/master/examples/chat/home.html).

+ 12 - 9
examples/chat/client.go

@@ -49,6 +49,10 @@ type Client struct {
 }
 
 // readPump pumps messages from the websocket connection to the hub.
+//
+// The application runs readPump in a per-connection goroutine. The application
+// ensures that there is at most one reader on a connection by executing all
+// reads from this goroutine.
 func (c *Client) readPump() {
 	defer func() {
 		c.hub.unregister <- c
@@ -70,13 +74,11 @@ func (c *Client) readPump() {
 	}
 }
 
-// write writes a message with the given message type and payload.
-func (c *Client) write(mt int, payload []byte) error {
-	c.conn.SetWriteDeadline(time.Now().Add(writeWait))
-	return c.conn.WriteMessage(mt, payload)
-}
-
 // writePump pumps messages from the hub to the websocket connection.
+//
+// A goroutine running writePump is started for each connection. The
+// application ensures that there is at most one writer to a connection by
+// executing all writes from this goroutine.
 func (c *Client) writePump() {
 	ticker := time.NewTicker(pingPeriod)
 	defer func() {
@@ -86,13 +88,13 @@ func (c *Client) writePump() {
 	for {
 		select {
 		case message, ok := <-c.send:
+			c.conn.SetWriteDeadline(time.Now().Add(writeWait))
 			if !ok {
 				// The hub closed the channel.
-				c.write(websocket.CloseMessage, []byte{})
+				c.conn.WriteMessage(websocket.CloseMessage, []byte{})
 				return
 			}
 
-			c.conn.SetWriteDeadline(time.Now().Add(writeWait))
 			w, err := c.conn.NextWriter(websocket.TextMessage)
 			if err != nil {
 				return
@@ -110,7 +112,8 @@ func (c *Client) writePump() {
 				return
 			}
 		case <-ticker.C:
-			if err := c.write(websocket.PingMessage, []byte{}); err != nil {
+			c.conn.SetWriteDeadline(time.Now().Add(writeWait))
+			if err := c.conn.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
 				return
 			}
 		}