Skip to content

Commit 6257d10

Browse files
committed
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.
1 parent 0b847f2 commit 6257d10

File tree

2 files changed

+23
-9
lines changed

2 files changed

+23
-9
lines changed

examples/chat/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,17 @@ there's an error writing to the websocket connection.
7272
Finally, the HTTP handler calls the client's `readPump` method. This method
7373
transfers inbound messages from the websocket to the hub.
7474

75+
WebSocket connections [support one concurrent reader and one concurrent
76+
writer](https://godoc.org/github.com/gorilla/websocket#hdr-Concurrency). The
77+
application ensures that these concurrency requirements are met by executing
78+
all reads from the `readPump` goroutine and all writes from the `writePump`
79+
goroutine.
80+
81+
To improve efficiency under high load, the `writePump` function coalesces
82+
pending chat messages in the `send` channel to a single WebSocket message. This
83+
reduces the number of system calls and the amount of data sent over the
84+
network.
85+
7586
## Frontend
7687

7788
The frontend code is in [home.html](https://github.com/gorilla/websocket/blob/master/examples/chat/home.html).

examples/chat/client.go

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ type Client struct {
4949
}
5050

5151
// readPump pumps messages from the websocket connection to the hub.
52+
//
53+
// The application runs readPump in a per-connection goroutine. The application
54+
// ensures that there is at most one reader on a connection by executing all
55+
// reads from this goroutine.
5256
func (c *Client) readPump() {
5357
defer func() {
5458
c.hub.unregister <- c
@@ -70,13 +74,11 @@ func (c *Client) readPump() {
7074
}
7175
}
7276

73-
// write writes a message with the given message type and payload.
74-
func (c *Client) write(mt int, payload []byte) error {
75-
c.conn.SetWriteDeadline(time.Now().Add(writeWait))
76-
return c.conn.WriteMessage(mt, payload)
77-
}
78-
7977
// writePump pumps messages from the hub to the websocket connection.
78+
//
79+
// A goroutine running writePump is started for each connection. The
80+
// application ensures that there is at most one writer to a connection by
81+
// executing all writes from this goroutine.
8082
func (c *Client) writePump() {
8183
ticker := time.NewTicker(pingPeriod)
8284
defer func() {
@@ -86,13 +88,13 @@ func (c *Client) writePump() {
8688
for {
8789
select {
8890
case message, ok := <-c.send:
91+
c.conn.SetWriteDeadline(time.Now().Add(writeWait))
8992
if !ok {
9093
// The hub closed the channel.
91-
c.write(websocket.CloseMessage, []byte{})
94+
c.conn.WriteMessage(websocket.CloseMessage, []byte{})
9295
return
9396
}
9497

95-
c.conn.SetWriteDeadline(time.Now().Add(writeWait))
9698
w, err := c.conn.NextWriter(websocket.TextMessage)
9799
if err != nil {
98100
return
@@ -110,7 +112,8 @@ func (c *Client) writePump() {
110112
return
111113
}
112114
case <-ticker.C:
113-
if err := c.write(websocket.PingMessage, []byte{}); err != nil {
115+
c.conn.SetWriteDeadline(time.Now().Add(writeWait))
116+
if err := c.conn.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
114117
return
115118
}
116119
}

0 commit comments

Comments
 (0)