Go 每日一庫:tproxy 是個啥?

你有同感嗎?

當大家在開發服務端代碼的時候,會不會經常有如下疑問?

複雜網絡情況的處理從來都是後端開發的重點和難點之一,你是不是也爲各種網絡情況的調試而頭頂發涼呢?

所以我寫了 tproxy

當我在做後端開發和寫 go-zero 的時候,經常會需要監控網絡連接,分析請求內容。比如:

tproxy 的安裝

GOPROXY=https://goproxy.cn/,direct go install github.com/kevwan/tproxy@latest

或者使用 docker 鏡像:

$ docker run --rm -it -p <listen-port>:<listen-port> -p <remote-port>:<remote-port> kevinwan/tproxy:v1 tproxy -l 0.0.0.0 -p <listen-port> -r host.docker.internal:<remote-port>

arm64 系統:

$ docker run --rm -it -p <listen-port>:<listen-port> -p <remote-port>:<remote-port> kevinwan/tproxy:v1-arm64 tproxy -l 0.0.0.0 -p <listen-port> -r host.docker.internal:<remote-port>

tproxy 的用法

$ tproxy --help
Usage of tproxy:
  -d duration
            the delay to relay packets
  -l string
            Local address to listen on (default "localhost")
  -p int
            Local port to listen on
  -q        Quiet mode, only prints connection open/close and stats, default false
  -r string
            Remote address (host:port) to connect
  -t string
            The type of protocol, currently support grpc

分析 gRPC 連接

tproxy -p 8088 -r localhost:8081 -t grpc -d 100ms

其中我們可以看到 gRPC 的一個請求的初始化和來回,可以看到第一個請求其中的 stream id 爲 1。

再比如 gRPC 有個 MaxConnectionIdle 參數,用來設置 idle 多久該連接會被關閉,我們可以直接觀察到時間到了之後服務端會發送一個 http2 的 GoAway 包。

比如我把 MaxConnectioinIdle 設爲 5 分鐘,連接成功之後 5 分鐘沒有請求,連接就被自動關閉了,然後重新建了一個連接上來。

分析 MySQL 連接

我們來分析一下 MySQL 連接池設置對連接池的影響,比如我把參數設爲:

maxIdleConns = 3
maxOpenConns = 8
maxLifetime  = time.Minute
...
conn.SetMaxIdleConns(maxIdleConns)
conn.SetMaxOpenConns(maxOpenConns)
conn.SetConnMaxLifetime(maxLifetime)

我們把 MaxIdleConns 和 MaxOpenConns 設爲不同值,然後我們用 hey 來做個壓測:

hey -c 10 -z 10s "http://localhost:8888/lookup?url=go-zero.dev"

我們做了併發爲 10QPS 且持續 10 秒鐘的壓測,連接結果如下圖:

我們可以看到:

這也就是我們經常會看到 MySQL 很多 TIME_WAIT 的原因。

然後我們把 MaxIdleConns 和 MaxOpenConns 設爲相同值,然後再來做一次相同的壓測:

我們可以看到:

這裏的 ConnMaxLifetime 一定要設置的小於 wait_timeout,可以通過如下方式查看 wait_timeout 值:

我建議設置小於 5 分鐘的值,因爲有些交換機會 5 分鐘清理一下空閒連接,比如我們在做社交的時候,一般心跳包不會超過 5 分鐘。具體原因可以看

https://github.com/zeromicro/go-zero/blob/master/core/stores/sqlx/sqlmanager.go#L65

其中 go-sql-driver 的 issue 257 裏有一段也在說 ConnMaxLifetime,如下:

14400 sec is too long. One minutes is enough for most use cases.

Even if you configure entire your DC (OS, switch, router, etc...), TCP connection may be lost from various reasons. (bug in router firmware, unstable power voltage, electric nose, etc...)

所以如果你不知道 MySQL 連接池參數怎麼設置,可以參考 go-zero 的設置。

另外,ConnMaxIdleTime 對上述壓測結果沒有影響,其實你也不需要設置它。

如果你對上述設置有疑問,或者覺得哪裏有誤,歡迎在 go-zero 羣裏一起討論。

項目地址

tproxy: https://github.com/kevwan/tproxy

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/_iZsFPaUk2RzHR_A2u-AdQ