#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で永続接続をしない例]]