你有同感吗?
当大家在开发服务端代码的时候,会不会常常有如下疑难?
- 纳闷 MySQL 连接池到底有多少连贯?
- 每个连贯的生命周期继续多久?
- 连贯异样断开的时候到底是服务端被动断的,还是客户端被动断的?
- 当长时间没有申请的时候,底层库是否有 KeepAlive 申请?
简单网络状况的解决素来都是后端开发的重点和难点之一,你是不是也为各种网络状况的调试而头顶发凉呢?
所以我写了 tproxy
当我在做后端开发和写 go-zero 的时候,常常会须要监控网络连接,剖析申请内容。比方:
- 剖析 gRPC 连贯何时连贯、何时重连,并据此调整各种参数,比方:MaxConnectionIdle
- 剖析 MySQL 连接池,以后多少连贯,连贯的生命周期是什么策略
- 也能够用来察看和剖析任何 TCP 连贯,看服务端被动断,还是客户端被动断等等
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
- 侦听在 localhost 和 8088 端口
- 重定向申请到
localhost:8081
- 辨认数据包格局为 gRPC
- 数据包提早 100 毫秒
其中咱们能够看到 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 秒钟的压测,连贯后果如下图:
咱们能够看到:
- 10 秒钟内建设了 2000+ 的连贯
- 过程中在不停的敞开已有连贯,重开新的连贯
- 每次连贯应用完放回去,可能超过 MaxIdleConns 了,而后这个连贯就会被敞开
- 接着来新申请去拿连贯时,发现连接数小于 MaxOpenConns,然而没有可用申请了,所以就又新建了连贯
这也就是咱们常常会看到 MySQL 很多 TIME_WAIT 的起因。
而后咱们把 MaxIdleConns 和 MaxOpenConns 设为雷同值,而后再来做一次雷同的压测:
咱们能够看到:
- 始终维持着 8 个连贯不变
- 压测完过了一分钟(ConnMaxLifetime),所有连贯被敞开了
这里的 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
go-zero: https://github.com/zeromicro/go-zero
欢送应用并 star 反对咱们!
微信交换群
关注『微服务实际 』公众号并点击 交换群 获取社区群二维码。