用 Systemtap 统计 TCP 连接
转自: https://mp.weixin.qq.com/s?__biz=MzIxMjAzMDA1MQ==&mid=2648946009&idx=1&sn=3a0be2fe4f098de819f7dc05b79c593f&chksm=8f5b5255b82cdb43fa8714aed8c351306596492396467bf85d8e14ee476e944e5e58bb0ba724#rd
问题
做压力测试的时候希望可以在服务器上采样,能获得当前服务器的TCP连接数、连接耗费时间的最大值、最小值、均值。
传统解决思路
TCP连接采样最直接的方式是通过netstat统计ESTABLISHED
状态的TCP连接数。一般压力测试工具会提供一份报告说明发送请求的最大值、最小值、均值。
-
netstat原理是读取/proc/net/tcp给出当前服务器的TCP状态,当“列表”很长的时候它就要花费更多时间完成解析。采样到的数据不及时,而且nestat会消耗非常大的CPU,影响测试结果;
-
压力测试工具报告的请求耗费时间的最大值、最小值、均值包含了网络延时。压力测试工具会同时开启N个并发访问服务器,对于网络来说是有大量的数据包同时产生,很可能受限于测试机或者服务器的带宽,根本没有送到服务器或者是测试机来不及处理产生了延时;
另辟蹊径
最好的办法是通过内核Hook拦截某些关键的TCP函数,比如能找到类似tcp_connect、tcp_close的函数。遗憾的是TCP建立连接比较容易找到(无非是accept、tcp_v4_connect之类的),但是关闭部分却没有相应的函数。这个问题折腾了我不少时间,最后我借鉴了nestat的思路——通过TCP的状态判断。
至于Hook内核基本上没什么难度,祭出大杀器Systemtap就行了。
Systemtap是世界上最牛B的Linux内核调试工具(没有之一),如果你之前听说过DTrace那么可以把它理解为DTrace for Linux。使用它通过简单的几行代码我们就可以Hook到内核中的任何函数,不但可以“截获”参数甚至可以修改参数。
我定义了一个全局数组connection
,当TCP状态是ESTABLISHED
时我会把时间戳放到以客户端IP地址+端口为Key的关联数组中,当TCP状态是CLOSE、CLOSE_WAIT时根据IP地址和端口取出时间戳,计算出耗费的毫秒数放入累加变量spend_time中。为了统计当前并发数,程序中每隔1秒钟,修改report关联数组。
最后在程序结束的时候输出所有请求耗费时间的最大值、最小值和均值。
测试结果
通过ab简单的模拟一下效果,设置并发度300,总的请求数量是10000。
结果显示ab记录发送了10000次而实际上服务器多收到146次,这部分请求应该是ab认为失败其实已经成功的。服务器的峰值是179,远远没有达到300的并发度。
附上systemtap脚本地址
https://gist.github.com/fireflyc/cf82f27ebbfe1ff61c4f789b8b1b0d25
后记
这篇文章的目的其实是想让大家学习用systemtap。网上关于它的教程很多,所以再写一篇“教程”也不可能好到哪里去。所以介绍一个之前写过的一个脚本,希望能够抛砖引玉。
欢迎关注公众账号了解更多信息“写程序的康德——思考、批判、理性”