#TCP #connperf #golang #tuning #blog
connperfはなぜ -type ephemeral -proto tcp --rate 20000前後で、不安定になるのか。CPU利用率高騰の原因は?
- goのpprof tracerを実装する @done
- connect側のreadが完了しない。 => bufioを挟むとだめだった @done
connectのwrite => readをいれると詰まる。14500 conns/s ちょうど6コア中3コア使い切るタイミング。一旦詰まると、goroutineがどんどんたまっていくのではないか。
さくらのクラウドのスペック向上 6 core => 12 core @done
コア数上げてもだめだった。14500 conns/sで詰まる。
perf でみると__inet_check_establishedが支配的 https://github.com/tempesta-tech/tempesta/issues/1419
client側のsomaxconnが小さいままだった => 解決 => 解決してない。
- どの待ち行列が溢れているかを確認する
- perf-tools では `__inet_check_established()`が20%ほどを占める。
`net.ipv4.tcp_abort_on_overflow=1`というものがある。 https://www.slideshare.net/brendangregg/how-netflix-tunes-ec2-instances-for-performance p.33
- tcp_abort_on_overflowはlisten時のbacklogが溢れた場合に接続をリセットする https://man7.org/linux/man-pages/man7/tcp.7.html @done
15k conns/s で試したが、特にリセットするような挙動はなかった。
- tcpretransで再送を確認した
- TCP_NODELAY, TCP_QUICKACK, SO_LINGER, TCP_FAST_OPEN を試す @done
- TCP_NODELAY: Nagle Algorithmを無効にする。少量のデータであっても即座に送信する。 https://linux.die.net/man/7/tcp
Go言語ではデフォルトで有効になっている。https://golang.org/pkg/net/#TCPConn.SetNoDelay
- TCP_QUICK_ACK:
- クライアント側に設定した。https://github.com/yuuki/connperf/pull/10/commits/7456385b39fa984f280e5d901e3d8a1d7371a07f @done
- 設定方法はnet.Conn -> TCPConn -> syscall.RawConn -> rawconn.Controlでfdを手に入れてからsetsockoptする https://kechako.dev/posts/2018/12/03/create-udp-conn-each-client/
- syscall.RawConnのプロポーザル [proposal: syscall: add Conn and RawConn interfaces #19435](https://github.com/golang/go/issues/19435)
- ACKはサーバでもやるので、サーバ側にも設定する https://github.com/yuuki/connperf/pull/10/commits/4245872799c9d0ad08a07abcafac4f0d5a617e87 @done
- 15k conns/s で実験するがかわりなし @done
- straceでQUICK_ACKが設定されているかチェックする sudo strace -f -e trace=setsockopt ./connperf connect --proto tcp --flavor ephemeral --rate 1 --duration 1000s 10.0.150.2:9100 @done
[pid 9470] setsockopt(7, SOL_TCP, TCP_NODELAY, [1], 4) = 0
[pid 9470] setsockopt(7, SOL_SOCKET, SO_KEEPALIVE, [1], 4) = 0
[pid 9470] setsockopt(7, SOL_TCP, TCP_KEEPINTVL, [15], 4) = 0
[pid 9470] setsockopt(7, SOL_TCP, TCP_KEEPIDLE, [15], 4) = 0
[pid 9470] setsockopt(7, SOL_TCP, TCP_QUICKACK, [1], 4) = 0
QUICK_ACKが設定されていた。
他にも、SO_KEEPALIVEやTCP_KEEPINTVL、TCP_KEEPIDLEが設定されている。
- SO_LINGER: close(2)後に即座にtimeoutさせる https://man7.org/linux/man-pages/man7/socket.7.html
Go言語でSO_LINGERを設定する方法 https://golang.org/pkg/net/#TCPConn.SetLinger
- サーバとクライアントの両方に設定した。https://github.com/yuuki/connperf/pull/10/commits/c531614832c00ab74c2c2fff8247ce31d04493ef @done
- 実験する。=> 16k conns/s まではたえた。17k conns/sからは詰まる。少しは効果があった。 @done
- TCP_FAST_OPEN: 一度コネクションを貼った相手とは、TCPの3ウェイハンドシェイク中にデータを送受信できるようになる。クライアントからSYNとともにデータを送信することで、実際にデータを送受信開始するまでの待ち時間が短縮できる。 https://asnokaze.hatenablog.com/entry/2017/05/09/223534
- Go言語で設定する方法を調べる。 @done
clientとserverで設定するために `echo 3 > /proc/sys/net/ipv4/tcp_fastopen` を設定。 https://github.com/bradleyfalzon/tcp-fast-open
connectを実行せず、いきなりsocketでソケットを作成して、いきなりsendtoのオプションに設定する。`syscall.Sendto(c.fd, data, syscall.MSG_FASTOPEN, sa)` https://github.com/bradleyfalzon/tcp-fast-open/blob/master/client.go#L39
https://christina04.hatenablog.com/entry/go-so-reuseport の記事にListenConfigでsocket optionを設定できそう。
net: add support for TCP Fast Open https://github.com/golang/go/issues/4842 経由でhttps://github.com/shadowsocks/go-shadowsocks2/pull/162 にGoでTCP_FASTOPENを設定するコードがあった。
- FAST_OPENを設定した。 https://github.com/yuuki/connperf/pull/10/commits/c68c9a85d4c399cf03dcff7c13fe704d432b9791 @done
- 実験の結果、19.5k conns/sまではスケールし、20k conns/sでは試行によっては詰まったりつまらなかったりした。 @done
- さくらのクラウドのVM 1コアクロックをあげる => migrate to tokyo 2nd region @done
東京第2リージョンへ移行し、CPUがIntel(R) Xeon(R) Gold 6212U CPU @ 2.40GHzへ
- 実験の結果、21 k conns/s まで耐えるようになった。 @done
[[PHPで永続接続をしない例]]