Jelajahi Sumber

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 9 tahun lalu
induk
melakukan
6257d10a8b
2 mengubah file dengan 23 tambahan dan 9 penghapusan
  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
 			}
 		}