Chinaunix首页 | 论坛 | 博客
  • 博客访问: 101985
  • 博文数量: 18
  • 博客积分: 681
  • 博客等级: 中士
  • 技术积分: 295
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-17 13:33
文章分类
文章存档

2012年(8)

2011年(10)

分类: LINUX

2012-01-09 21:02:08

上次有个运维的同学让帮忙分析下tcpconn代码,他想用tcpconn插件来统计当前服务器80端口TCP链接数。但collectd加载了tcpconn插件后,cpu的load突然飙升,而没有使用tcpconn插件的时候load很低。想先从tcpconn代码上看看能否找到使得cpu load飙升的原因。

首先来简要介绍下collectd。collectd - The system statistics collection daemon。其官网上是这么解释的,是一个后台进程,定期统计系统性能数据,能够多种方式地存储这些性能数据,供报表输出,这些统计数据可以用来寻找当前系统的性能瓶颈,并预测未来系统的load。下图为collectd统计得到数据的一张图表输出。

graph-cpu

 

是collectd的一个插件,可以统计当前服务器某个端口的TCP链接数,其既可以统计连进当前系统某个端口的TCP数,也可以统计当前系统连出的TCP数,前面的端口称为localport,而后面的端口称为remoteport。tcpconn不仅支持linux系统,而且支持bsd系列(freebsd,openbsd,netbsd,mac os x)的系统,本文为了简单起见,只分析linux系统下tcpconns。linux下tcpconn实现是基于/proc文件系统的,因此有必要对/proc做一个最简单的介绍。

/proc文件系统(/procfs)是个虚拟的文件系统,我们可以读取/procfs内的某个文件得到当前内核存储的系统信息,也可以通过向/procfs内的某个文件写入一些数据控制内核的行为。如/proc/cpuinfo可以输出cpu信息,/proc/net/tcp提供了当前系统的TCP链接信息,实际上tcpconns就是通过解析/proc/net/tcp和/proc/net/tcp6来统计当前系统某个端口的TCP链接数的。 每次读取/procfs内文件的时候,内核会动态地生成文件内容。

/proc/net/tcp文件格式:
sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode                                               
0: 00000000:00C7 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 10591797 1 ecb4c940 300 0 0 2 -1                  
1: 00000000:2FA8 00000000:0000 0A 00000000:00000000 00:00000000 00000000  1801        0 10819883 1 e51a04c0 300 0 0 2 -1                  
2: 00000000:044A 00000000:0000 0A 00000000:00000000 00:00000000 00000000  1801        0 10818963 1 c5a34dc0 300 0 0 2 –1

上述字段中,几个较重要的字段为:

local_address 00000000:00C7 tcp链接的本地地址,冒号前面8位(十六进制)的ip地址,冒号后面的4位(十六进制)的端口,

rem_address 00000000:0000 tcp链接的远程地址,冒号前面8位(十六进制)的pi地址,冒号后面的4位(十六进制)为端口,

st 0A tcp连接的状态,总共有十一种状态,TCP链接的状态转换图可以参考。

 

tcpconn插件读取/proc/net/tcp文件,就是分析上述格式的文件,得到某个端口的TCP链接数。tcpconns的基本数据结构为端口节点链表,端口节点定义如下:

typedef struct port_entry_s
{
  uint16_t port; // 端口
  uint16_t flags; //
  uint32_t count_local[TCP_STATE_MAX + 1];  // local TCP连接数,不同的状态存放在不同的数组元素中
  uint32_t count_remote[TCP_STATE_MAX + 1]; // remote TCP连接数
  struct port_entry_s *next;
} port_entry_t;

static port_entry_t *port_list_head=NULL;

tcpconns读取/proc/net/tcp文件,一行一行地分析,得到一个port_entry_s链表,然后把统计结果提交给collectd。如要统计localport=80的端口号的tcp连接数,那么tcpconns会读取每行,判断localport是否为0x50,如果是就给0x80的端口的count_local[st]加1,其中st为tcp链接的状态。

load飙高原因假设

为什么用了tcpconns插件后,cpu的load就飙高,最终我们做了以下假设并进行了分析:

  1. 要统计的80端口tcp连接数较多。从前面的代码实现来看,不论端口是什么,都会parse一遍/proc/net/tcp文件,因此不可能这个原因;
  2. /proc/net/tcp文件较大,导致每次parse都耗费了过多的cpu,这有可能。我们查看了下线上的机器,tcp链接数在10000以上,parse一遍这个文件需要多少时间?
  3. tcpconn调用次数过于频繁,前面提到collectd每隔一段时间就去查看下当前系统性能,如果调用频率过高,那么对cpu性能肯定会造成影响。但collectd还统计系统其它性能,其调用频率也很高,为什么在加载tcpconn插件之前,系统的load较低呢?

虽然还没有得到最终的原因,但我在这里可以做个猜想:引起cpu load飙高的原因是前面提到第2点和第3点的综合,即tcpconns被调用的频率高,而且每次要分析10000多行的文件。tcpconn调用频率高本身并不会存在什么问题,如调用/proc/cpuinfo的频率再高也不会存在性能问题,因为/proc/cpuinfo文件较小,而是在内核中是固定不变的;而系统的tcp连接数是实时变化的,意味着linux内核须维护一个tcp连接的数据结构,每次调用/proc/net/tcp时,内核都会生成tcp文件,同时因为文件较大,使得tcpconn分析也较耗时,最终使得load飙升。

阅读(3301) | 评论(1) | 转发(0) |
0

上一篇:类型

下一篇:EUnit例子

给主人留下些什么吧!~~

jndxjing2013-03-14 14:58:06

想请问下您怎么让collectd的图标输出,统计了关于CPU的所有信息在一张图中?是代码实现还是利用了其他的工具?谢谢您的解答,最近正在研究这个。希望能够得到您的回答。