-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Correct way to save memory using write buffer pool and freeing net.ht…
…tp default buffers (#761) **Summary of Changes** 1. Add an example that uses the write buffer pool The loop process of the websocket connection is inner the http handler at existing examples, This usage will cause the 8k buffer(4k read buffer + 4k write buffer) allocated by net.http can't be GC(Observed by heap profiling, see picture below) . The purpose of saving memory is not achieved even if the WriteBufferPool is used. In example bufferpool, server process websocket connection in a new goroutine, and the goroutine created by the net.http will exit, then the 8k buffer will be GC. ![heap](https://user-images.githubusercontent.com/12793501/148676918-872d1a6d-ce10-4146-ba01-7de114db09f5.png) Co-authored-by: hakunaliu <hakunaliu@tencent.com> Co-authored-by: Corey Daley <cdaley@redhat.com>
- Loading branch information
1 parent
8983b96
commit 8039329
Showing
3 changed files
with
145 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
//go:build ignore | ||
// +build ignore | ||
|
||
package main | ||
|
||
import ( | ||
"flag" | ||
"log" | ||
"net/url" | ||
"os" | ||
"os/signal" | ||
"sync" | ||
"time" | ||
|
||
"github.com/gorilla/websocket" | ||
) | ||
|
||
var addr = flag.String("addr", "localhost:8080", "http service address") | ||
|
||
func runNewConn(wg *sync.WaitGroup) { | ||
defer wg.Done() | ||
|
||
interrupt := make(chan os.Signal, 1) | ||
signal.Notify(interrupt, os.Interrupt) | ||
|
||
u := url.URL{Scheme: "ws", Host: *addr, Path: "/ws"} | ||
log.Printf("connecting to %s", u.String()) | ||
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil) | ||
if err != nil { | ||
log.Fatal("dial:", err) | ||
} | ||
defer c.Close() | ||
|
||
done := make(chan struct{}) | ||
|
||
go func() { | ||
defer close(done) | ||
for { | ||
_, message, err := c.ReadMessage() | ||
if err != nil { | ||
log.Println("read:", err) | ||
return | ||
} | ||
log.Printf("recv: %s", message) | ||
} | ||
}() | ||
|
||
ticker := time.NewTicker(time.Minute * 5) | ||
defer ticker.Stop() | ||
|
||
for { | ||
select { | ||
case <-done: | ||
return | ||
case t := <-ticker.C: | ||
err := c.WriteMessage(websocket.TextMessage, []byte(t.String())) | ||
if err != nil { | ||
log.Println("write:", err) | ||
return | ||
} | ||
case <-interrupt: | ||
log.Println("interrupt") | ||
|
||
// Cleanly close the connection by sending a close message and then | ||
// waiting (with timeout) for the server to close the connection. | ||
err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) | ||
if err != nil { | ||
log.Println("write close:", err) | ||
return | ||
} | ||
select { | ||
case <-done: | ||
case <-time.After(time.Second): | ||
} | ||
return | ||
} | ||
} | ||
} | ||
|
||
func main() { | ||
flag.Parse() | ||
log.SetFlags(0) | ||
wg := &sync.WaitGroup{} | ||
for i := 0; i < 1000; i++ { | ||
wg.Add(1) | ||
go runNewConn(wg) | ||
} | ||
wg.Wait() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
//go:build ignore | ||
// +build ignore | ||
|
||
package main | ||
|
||
import ( | ||
"flag" | ||
"log" | ||
"net/http" | ||
"sync" | ||
|
||
_ "net/http/pprof" | ||
|
||
"github.com/gorilla/websocket" | ||
) | ||
|
||
var addr = flag.String("addr", "localhost:8080", "http service address") | ||
|
||
var upgrader = websocket.Upgrader{ | ||
ReadBufferSize: 256, | ||
WriteBufferSize: 256, | ||
WriteBufferPool: &sync.Pool{}, | ||
} | ||
|
||
func process(c *websocket.Conn) { | ||
defer c.Close() | ||
for { | ||
_, message, err := c.ReadMessage() | ||
if err != nil { | ||
log.Println("read:", err) | ||
break | ||
} | ||
log.Printf("recv: %s", message) | ||
} | ||
} | ||
|
||
func handler(w http.ResponseWriter, r *http.Request) { | ||
c, err := upgrader.Upgrade(w, r, nil) | ||
if err != nil { | ||
log.Print("upgrade:", err) | ||
return | ||
} | ||
|
||
// Process connection in a new goroutine | ||
go process(c) | ||
|
||
// Let the http handler return, the 8k buffer created by it will be garbage collected | ||
} | ||
|
||
func main() { | ||
flag.Parse() | ||
log.SetFlags(0) | ||
http.HandleFunc("/ws", handler) | ||
log.Fatal(http.ListenAndServe(*addr, nil)) | ||
} |