不愿意看过程的直接跳到本文的最后吧。
虽然很多时候我们知道,出来混要还这句话,但是,不得不承认的是,很多时候都是到病入膏肓的时候才来解决问题。比如下面要说的由 netstat 引起的问题。
如果经常处理一些连接数很多的机器就会发现,使用 netstat 查看当前状态,返回的结果会非常的慢,有时候可能 1m 都返回不了结果。比如下面这个最经典的命令:
$ time sudo netstat -tunlp
Active Internet connections (only servers)
…
tcp 0 0 x.x.x.x:80 0.0.0.0:* LISTEN 1278/nginx
…
real 0m12.846s
user 0m0.300s
sys 0m12.490s
上面这个就花了 13s 来返回结果。通过 strace 跟踪发现,netstat 的 input 就是 /proc/net/tcp:
$ sudo strace netstat -tunlp
…
open("/proc/net/tcp", O_RDONLY) = 3
read(3, " sl local_address rem_address "…, 4096) = 4050
read(3, " 26: A11A91D5:0010 5B248875:7E7"…, 4096) = 4050
read(3, " 53: A11A91D5:0010 EF1C0CAB:C7A"…, 4096) = 4050
read(3, " 80: A11A91D5:0010 4D1E8875:3AD"…, 4096) = 4050
read(3, " 107: A11A91D5:0010 6525CFDD:C1A"…, 4096) = 4050
read(3, " 134: A11A91D5:0010 C2EF53DF:82F"…, 4096) = 4050
read(3, " 161: A11A91D5:0010 62803571:585"…, 4096) = 4050
…
因此如果该文件的条目非常的多(10w+),经常要等几十秒得到结果也就很正常了。
很不幸的是,zabbix 使用的 net.tcp.listen 这个 item 调用的就是 /prco/net/tcp 这个文件,通过做匹配获取指定的端口,速度可想而知。 造成的结果是经常在 1min 内拿不到数据,造成报警。具体的代码可以到 src/libs/zbxsysinfo/linux/net.c 定义的 NET_TCP_LISTEN 函数去 grep 一下。
而很早之前,我就介绍过一篇使用 ss 来取代 netstat 的博客。跟 netstat 最大的不同就是读取的文件不同,因而速度也快的多。看 ss 的 man 可以发现,ss 调用的是 tcp_diag 模块,这个模块具体怎么操作的不是很清楚,我更关心的是他是如何提升效率的。要实现跟上面一样的效果,像下面这样就可以了:
$ sudo ss -lp | grep mongo
0 128 *:27700 *:* users:(("mongod",3187,10))
0 128 *:27701 *:* users:(("mongod",2050,13))
0 128 *:27703 *:* users:(("mongod",20278,12))
0 128 *:27704 *:* users:(("mongod",20379,13))
0 128 *:27705 *:* users:(("mongod",2099,12))
0 128 *:27706 *:* users:(("mongod",11363,12))
0 128 *:27707 *:* users:(("mongod",2293,12))
0 128 *:28700 *:* users:(("mongod",3187,11))
0 128 *:27708 *:* users:(("mongod",2428,12))
0 128 *:27709 *:* users:(("mongod",2442,13))
0 128 *:28701 *:* users:(("mongod",2050,14))
0 128 *:27710 *:* users:(("mongod",2779,12))
0 128 *:28703 *:* users:(("mongod",20278,14))
格式化不是很好,tr 处理一下就好了:
$ sudo ss -lp | tr -s ' ' '\t'
说了这么多,其实应到到实际上就是,zabbix 在监控有大量连接机器的时候,如果使用起原生的 net.tcp.listen 等涉及到调用 netstat 的 item,其实准确的说并没有调用 netstat,而是直接 fopen 的 /proc/net/tcp 文件,就会产生上面的问题。办法就是自己写套 template,换成调用 ss 的方式。除此之外,如果使用 LLD(low level discovery),同样的记得别用 netstat 发现监听的端口,改用 ss 既可,对应的脚本可以在这里找到。
结论:
不管什么时候,请使用 ss 而非 netstat 来查看目前系统的状态。