读取 /proc/net/tcp 的问题

不愿意看过程的直接跳到本文的最后吧。
虽然很多时候我们知道,出来混要还这句话,但是,不得不承认的是,很多时候都是到病入膏肓的时候才来解决问题。比如下面要说的由 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 来查看目前系统的状态。

  • 章炎

    netstat所有的功能ss都能满足吗?

    • http://jaseywang.me/ Jasey Wang

      我现在常用的都能满足。。。

      • 章炎

        Great!

  • harveyzh

    都有 p 选项的情况下,ss -lnpt 也有比 netstat -lnpt 慢的时候,那是在使用淘宝的 TFS 的文件服务器上,每个 dataserver 进程打开了非常多的文件。