现在业务有 15 台服务器做负载均衡,nginx 轮询方案。其中有 3 台服务器,对 kafka 集群、mysql 、redis 的连接数,要大于其他的服务器。造成这 3 台的接口响应时长比较长。
服务器配置相同( 8 核 8G );
跑的项目相同:
php 语言,hyperf2.0 框架,接口整体 qps 目前在 9000 左右,单机负载 qps600 左右。
服务器承载的 qps 相近,都是 600 左右。
新建服务器时,用的同一个镜像,但是都进行过一些 tcp 参数调优:
echo '1200'> /proc/sys/net/ipv4/tcp_keepalive_time
echo '8192'> /proc/sys/net/ipv4/tcp_max_syn_backlog
echo '10000'> /proc/sys/net/ipv4/tcp_max_tw_buckets
echo '1'> /proc/sys/net/ipv4/tcp_tw_reuse
echo '30'> /proc/sys/net/ipv4/tcp_fin_timeout
echo '1024 65000'> /proc/sys/net/ipv4/ip_local_port_range
echo '131072' > /proc/sys/net/ipv4/tcp_max_orphans
echo '383652 511537 767304' > /proc/sys/net/ipv4/tcp_mem
echo '32768' > /proc/sys/net/ipv4/tcp_max_orphans
echo '93723 124966 187446'> /proc/sys/net/ipv4/tcp_mem
下面是两台服务器连接数的情况:
#正常的服务器
941 10.10.45.199
357 10.10.55.43
356 10.10.28.148
353 10.10.85.51
320 10.10.59.7
#异常的服务器
771 10.10.45.199
652 10.10.55.43
651 10.10.28.148
649 10.10.85.51
442 10.10.58.73
# 45.199 是 nginx 服务器;排在前 2 、3 、4 的是 Kafka 集群;可见异常的服务器连接数比正常的服务器要多
TOP命令观察 左边为正常服务器,右边为异常服务器。异常服务器的php进程cpu使用率明显比其他的高。
去查看hyerf日志:
异常服务器的此类错误输出速度比其他的服务器要快
去swoole文档中查看此类型错误,文档中说明:
服务端响应时,客户端已经切断了连接导致
常见于:
浏览器疯狂刷新页面 (还没加载完就刷掉了)
ab 压测到一半取消
wrk 基于时间的压测 (时间到了未完成的请求会被取消)
以上几种情况均属于正常现象,可以忽略,所以该错误的级别是 NOTICE
如由于其它情况无缘无故出现大量连接断开时,才需要注意
1
defunct9 2021-11-08 14:21:31 +08:00
开 ssh ,让我上去看看
|
2
tcpdump 2021-11-08 14:32:37 +08:00
容器内的内核参数也生效?
|
4
defunct9 2021-11-08 16:50:51 +08:00
参数还可,没看出大毛病。异常的服务器 nginx 连过来的少,主动连 kafka 的很多。怪异
|
5
nekoyaki 2021-11-08 17:43:50 +08:00 1
tcp 优化参数里 net.ipv4.tcp_tw_recycle 和 net.ipv4.tcp_timestamps 的值分别是什么?
一个是,很多以讹传讹的旧文章里会把 tcp_tw_recycle 和 tcp_tw_reuse 都打开,这是有问题的。tcp_tw_recycle 除非你完全了解了它,否则一定要关掉(置为 0 )。新的 linux 内核里已经废弃了这个参数。你需要确认这个数值设定成了 0 。 该参数工作时,有时会误切断正常的连接。 另一个,是 tcp_tw_reuse 如果想要有效地开启,那么一定要同时把 net.ipv4.tcp_timestamps 设置为 1,否则无效; 但如果 net.ipv4.tcp_timestamps 为 1 时,若内网的服务器之间的时间不同步,则在负载均衡其后面时,就有可能造成连接异常断开。 因此,一定要确认 net.ipv4.tcp_timestamps =1 且 tcp_tw_reuse = 1 且. cp_tw_recycle = 0 且各个服务器都正确地设置了时间同步(这里要使用 ntpd 而不是 ntpdate ,以防造成时间跳跃)。 |
6
latteczy 2021-11-08 17:58:49 +08:00 1
单机 600 的 qps 还不至于要优化内核参数吧🐶
|
7
sujin190 2021-11-08 18:00:20 +08:00
我怎么感觉原因结果弄反了,你确定不是因为接口相应慢,所以导致创建了更多的连接么,对于 swoole 这样的应用来说,cpu 占用过高会导致每个请求存活时间变长,也即意味着有更多请求在同时处理,当然会创建更多数据库连接了
所以 swoole 这样的应用想要低延时平稳,其实是要依据后端数据库啥的处理延时限制并发的,也即并发到一定程度后并发的上升并不能提高 qps ,而是每个请求的延时增加 |
8
yuandj OP @sujin190 同负载的情况,多台服务器,只有个别的 3 台服务器链接数会比别的多。“个别服务器比别的服务器处理的慢,从而导致接了更多请求,导致请求时间延长”,这个想来也有道理,但具体原因还需要再找一下。感谢指点
|
9
yuandj OP @nekoyaki tcp_timestamps 和 tcp_tw_reuse 都为 1 ,tcp_tw_recycle 的值没有查出来,应该已经废弃了
|
10
pmispig 2021-11-08 22:56:37 +08:00
你这个情况,还没到要优化内核参数的地步。
其中有 3 台服务器,对 kafka 集群、mysql 、redis 的连接数,要大于其他的服务器。感觉是这 3 台承载的请求更多。 |
11
yuandj OP @latteczy 我感觉也是啊,为什么别人的服务器都这么 NB ,2 核 4G 都能抗 1000qps 。。。我这服务器抗 500qps ,ss -a 查看 tcp 连接数时,达到 8000 多就不行了
|
12
yuandj OP @pmispig 从监控面板看,和其他服务器 qps 是相近的,但连接数却多 40%左右。nginx 用的轮询,也没加权重,感觉 7 楼说的有一定道理,但为什么这台处理的要比其他的慢,这个问题还需要排查。从监控面板( grafana )可以看到,在平时单机 500qps 时,虽然没有发生响应延长,但是异常的服务器 system load 在 120%左右,其他的都是在 40%左右。
|
13
pmispig 2021-11-08 23:12:39 +08:00
@yuandj 搞个火焰图之类的对比下两边 CPU 高的函数。话说你一台应用服务器连 kafka 这么多连接,都不正常吧?
一般连 kafka ,感觉应该是分配到几个分区就几个连接 |
15
yuandj OP 用 “strace -cp 进程 id” 追了一下 swoole worker 的系统调用
% time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 32.87 0.404515 4 103647 gettimeofday 20.26 0.249291 5 53990 34800 recvfrom 14.14 0.174060 10 18136 sendto 10.83 0.133311 4 33749 clock_gettime 10.02 0.123303 5 26864 epoll_ctl 7.08 0.087140 8 11096 epoll_wait 1.46 0.017924 5 3798 1033 read 目前怀疑和定时器有关 |
16
liuxu 2021-11-09 11:07:38 +08:00
我先给出问题的结论:
你的 kafka 集群中,存在某个单节点写入过慢 /负载过高。而且你的 kafka 负载均衡被连接后会固定写入某个到了某个节点。 首先和你的内核参数关系不大,qps 600 只要 ulimit -n 调大一点,就一切 ok 。 根据你提供的信息,我整理下现状: 1. 有问题的服务器 php 和 ruby 的 cpu 占用都大,但是 cpu 使用率都没有达到 100%。 2. 有问题的服务器 swap 占用过多,但不明显,只有 100MB 。 3. 有问题的服务器存在大量 connection 存在 dose not exists 。 4. nginx 是轮询 php 后端。 5. php 业务链接 kafka/mysql/redis 。 6. 有问题的 swoole 对 kafka 有多 1 倍的连接数,对 nginx 的连接数比正常的少 20%。 7. 只有 3 台固定的 swoole 有以上问题。 8. 有问题的 swoole 请求时间过长。 下面说下问题解析,如果以上信息没有提供错误的话,可以得到以下结论: 一、 主要现象是 php 接口过慢。所以一下现象可以正常解释: 1. 会出现大量 connection 错误。因为接口慢,所有 client 等不及关掉连接,或者刷新。 2. php 、ruby(fluentd)、supervisord 的 cpu 过高,但是 ruby 内存占用小。因为大量 connection 错误,所以 swoole 频繁写入日志,从而导致 fluentd 频繁调起读取日志发送或写入 buffer 。而日志都是 connnect 错误,字符少,所有内存占用少。同时我可以推测你的 fluentd 是 supervisord 管理的,所以 supervisord 频繁调起 fluentd ,导致它自己的 cpu 也高。 二、另一个现象是 3 台固定的 swoole 有问题。这个问题可以排除和定位问题: 1. nginx 没问题。虽然我推测可能是 nginx 负载均衡算法问题,或者说 nginx 轮询没有真实的运行,但是这个是解释不通的。首先 nginx 基本不可能出问题,有问题也极难遇到,当然我说的是你用的 stable 版。即使是 nginx 问题,那么出现 3 台固定 swoole 有问题,只有可能是有人用固定几个 ip 在 cc 攻击,才会导致 nginx 转发到这 3 台 swoole 。但是你 600 的 qps ,能 cc 0.6k 我还没见过这么抠的。 2. swoole 业务代码自身没问题。如果是 woole 业务代码逻辑问题,那么问题应该是随机出现在不同 swoole 服务器上,而不是固定 3 台。 3. swoole 所在物理服务器没问题。可能是内存颗粒有问题,导致处理业务数据过慢。但是我想你用的应该是 kvm 云服务器。而 3 台都落在一台云服务物理服务器可能性不大,即使存在云服务商应该能检查出来,会即使通知你,并更换内存条。当然也有极微可能是这个问题。 4. swoole 所在屋里服务器随机 io 满负荷。这个是有可能的,可能是你这 3 台 swoole 由于 connnection 错误日志太多,php 写入和 fluentd 读取导致 io 爆满。但是你的是 8H8G 的服务器,而且只有 600qps ,所有基本不可能。 三、为什么我推测是 kafka 单节点有问题。 1. 不是 mysql 问题,如果是 mysql 问题,例如业务 sql 读取大量数据,问题应该是随机出现在不同 swoole 服务器上。 2. 不是 redis 问题,原因如上。 3. kafka 情况有点不同,我推测你的 swoole 和 kafka 之间是 swoole 启动时和 kafka 创建的长连接,所以这 3 台固定写入到某个节点,而这个节点写入过慢,写入过慢所以 swoole 会维持连接,你看到的连接数就会更多。同时写入过慢所以你的 php 接口慢,这就都解释通了。 |
17
yuandj OP @liuxu
1 、swooler 的控制台日志并没有往 fluentd 里发送,发送的都是业务日志,并且量不小。目前接收的日志服务器确实也存在问题,很早之前搭建的,单核处理的,这么大量的日志目前打包不过来,所以偶尔会出现 ruby 占用内存过高的问题。 2 、这台服务器不止对 kafka 连接数比较多,对 mysql 和 redis 也比其他的服务器要多,我只是列出了 kafka 的。 3 、大多时间异常服务器对 nginx 的连接数是要比正常服务器多的。截图时截太巧了。。 4 、目前每次请求,都会从 Kafka 连接池拿一个链接写入数据,Kafka 连接池并不是服务初始化的,而是有业务请求时去创建的。这样看来从 Kafka 连接池初始化这块逻辑有优化的空间。 5 、目前正在使用 strace 追 worker 进程的问题,发现异常服务器的进程调用 gettimeofday(swooler 定时器)比较多,猜测是业务的定时任务落在了此服务器上,打算把定时任务固定到一台服务器试一下。 6 、感谢指点 |
18
yuandj OP @liuxu Kafka 写入慢,导致连接数增加,这个是合理的。但写入 Kafka 时用了协程,并不会影响到主协程的速度。
|
19
liuxu 2021-11-09 12:34:54 +08:00
@yuandj 根据你#17 的信息,我也觉得可能是 timer 的业务问题,由于 linux 内核的调度器频繁把 cpu 切到定时间的线程执行任务,导致影响了接口主线程执行。但是你的 cpu 负载并没有满,只用了 50%左右,这很奇怪,会有这么大的影响么。还是说你的定时器任务和接口中的代码有关联,导致接口需要等待定时器的任务结果。
如果 swoole 方面不好解决,可以把 nginx 的负载均衡改成最小连接数应急,不过这个方案你应该也知道 |
20
liuxu 2021-11-09 16:14:00 +08:00
@yuandj 突然想起来,如果你没能通过 Timer 解决问题,我建议你再次从 kafka/mysql/redis 阻塞分析,原因是你 kafka 使用的是协程而不是异步任务,协程只能解决单线程 io 阻塞问题,只是能让程序并行处理多个任务,并不能让单个请求的时间减少(并不是多线程执行,请求的线程把任务扔给任务线程后直接返回请求 response )
如果你 kafka 节点有问题的话,一个请求中,协程依然会等待任务执行完(等待途中 yield 让别的请求执行),等待结束继续处理这个请求,然后返回请求。如果你的框架请求一开始就创建了 mysql 和 redis 连接而没有主动释放,kafka 的阻塞会导致 swoole 维持它们,所有 kafka/mysql/redis 连接数都多。因为 swoole 执行慢,所以 nginx 连接维持时间长,连接看上去也会多 当然还是希望是 Timer 问题,事情就简单很多 |
21
geligaoli 2021-11-09 16:38:01 +08:00
几百的连接数,不 TCP 调优也足够用了。还是看看接口中的业务逻辑调用吧,估计是业务处理慢,连接也就释放不了
|
22
yuandj OP @liuxu 排除了一下,不是定时器的问题。
后来尝试把异常的三台服务器的 swoole worker number 调整为了 8 ,其他的是 16 ,初步观察变得正常了,所以推测还是程序处理慢,cpu 切换频繁带来的性能消耗。 但是如果是 redis 或者 kafka 响应慢,也不应该只出现在这几台服务器上。所以这个问题还需要继续排查 |
23
liuxu 2021-11-10 15:37:58 +08:00
@yuandj
mysql/redis 不太可能,他们是单机,如果有问题,应该是你所有 swoole 随机出现问题 我之所推测是 kafka 的问题,是因为你的 kafka 是集群,而集群中一个 topic 有多个 partition ,而且 kafka 可以指定 key ,我是推测你是写入到某个特定的 partition 了,而这个 partition 所在服务器的磁盘 io 爆了,或者内存爆了大量 swap 占用 但是也只是根据已有信息做的推测,更准确的定位方法起码得知道 nginx 配置,nginx 服务器硬件 benchmark ,php 接口 profile (分析所有外部 io 调用,kafka ,mysql 等等),php 机器 benchmark ,kafka 服务器 benchmark 最重要的就是 php 的 profile ,你给出的信息还是太少了 |
24
liuxu 2021-11-10 15:41:58 +08:00
@yuandj 我不知道为什么你把 swoole 的 worker 16 改成 8 就正常了,据我的经验,你的系统是 io 任务处理不及时导致,如果减少 worker ,或导致 nginx 随机抛出 502 timeout ,因为 swoole 来不及处理所有请求返回,目前来看是很奇怪的
|
25
yuandj OP @liuxu worker 个数和 cpu 核心数一致,可以减少 CPU 切换的 IO
比如 8 核机器,开 16 个 worker ,那么这 8 核需要在 16 个 worker 中来回切换 如果只开 8 个 worker ,一个核对一个 worker ,就减少了 CPU 的切换 io 之所以没有出现 502 ,可能是因为目前的量没到达顶峰。并且到达顶峰后,也不会报 502 ,表现是 worker 处理的慢,接口响应增长,nginx 那边超时断连,会返回 499. |
26
liuxu 2021-11-10 20:10:19 +08:00
@yuandj 额。。CPU 切换导致额外的性能消耗,你说的原理是没错。。但是你的实际情况是你 CPU 都没跑满,才 50%,只有负载过了 100%才会逐渐显露上下文切换衰减问题。。
http 的语义,4xx 是客户端错误,worker 处理慢 nginx 超时断开连接,会返回 5xx 服务端错误。qps 上限最开始会返回 502 Bad Gateway ,也就是 timeout ,后面压力越来越大 swoole 彻底崩了后,nginx 一般是返回 503 Service Unavailable 。。 你不跑 profile 再怎么分析都是猜拳,还是希望你自己能早点彻底解决吧 |