前面我们讨论系统调用的时候结论是耗时200ns-15us不等。不过我今天说的我的这个遭遇可能会让你进一步认识系统调用的真正开销。在本节里你会看到一个耗时2.5ms的connect系统调用,注意是毫秒,相当于2500us!

问题描述

当时是我的一个线上云控接口,是nginx+lua写的。正常情况下,单虚机8核8G可以抗每秒2000左右的QPS,负载还比较健康。但是该服务近期开始出现一些500状态的请求了,监控时不时会出现报警。通过sar -u查看峰值时cpu余量只剩下了20-30%。

图3.jpg

第一步、迅速锁定嫌疑人

top命令查看cpu使用,通过top命令发现峰值的时候cpu确实消耗的比较多,idle只有20-30%左右。在使用的cpu里,软中断的占比也比较高,1/3左右。

再通过cat /proc/softirqs查看到软中断是都是网络IO产生的NET_TX,NET_RX,和时钟TIMER。

既然软中断这个贼人吃掉了我这么多的CPU时间,所以案件的嫌疑人就这么初步被我锁定了。

处理,那既然是NET_TX,NET_RX和TIMER都高,那咱就挑可以削减的功能砍一砍呗。

  • 1.砍掉多余的gettimeofday系统调用
  • 2.每个请求砍掉一次非必须Redis访问,只留了必要的。

结果:峰值的cpu余量从确实多出来一些了。报警频率确实下来了,但是还是偶尔会有零星的报警。可见该嫌疑人并非主犯。。

第二步、干掉一大片,真凶在其中

接着查看网络连接的情况ss -n -t -a发现,ESTABLISH状态的链接不是很多,但是TIME-WAIT有11W多。继续研究发现针对..*.122:6390的TIME-WAIT已经超过了3W。所以端口有限。原来呀,上一步执行时只干掉了连接上的数据请求,但是tcp握手请求仍然存在。

处理:彻底干掉了针对..*.122:6390的网络连接请求,只保留了必须保留的逻辑。

结果:问题彻底解决。sar -u查看cpu的idle余量竟然达到了90%多。

Tips:单台机器如果作为TCP的客户端,有如下限制

  1. ESTABLISH状态的连接只能有ip_local_port_range范围内的个数。
  2. 只有针对特定ip,特定port的TIME-WAIT过多,超过或接近ip_local_port_range,再新建立连接可能会出现无端口可用的情况。( 总的TIME-WAIT过多并不一定有问题 )

没想到一个简单砍掉一个对redis server的tcp连接,能把cpu优化到这么多。大大出乎意料,而且也想不明白。 根据我之前的性能测试经验,每个tcp连接的建立大约只需要消耗36usec的cpu时间。我们来估算一下:

当时server的qps大约在2000左右,假设是均匀分布的,则8个核每个核每秒只需要处理250个请求。也就是说每秒一条tcp连接需要消耗的cpu时间为:250*36usec = 9ms.

也就是说,正常来讲砍掉这些握手开销只能节约1%左右的cpu,不至于有这么大的提升。(即使我上面的估算只考虑了建立连接,没有统计释放连接的cpu开销,但是连接释放cpu开销也和建立连接差不多。)

总之,这一步确实解决了问题,但是代价是牺牲了一个业务逻辑。

最终、审出真凶,真相大白于天下

我在某一台机器上把老的有问题的代码回滚了回来,恢复问题现场。然后只修改一下ip_local_port_range。 然后请出了strace这个命令。

通过strace -c 统计到对于所有系统调用的开销汇总。 结果我们发现了connect系统调用这个二货,在正常的机器上只需要22us左右,在有问题的机器上竟然花掉来 2500us,上涨了100倍。我们用strace -c $PID查看一下出问题时和正常时的connect系统调用耗时对比:

图1.png

<centor>图1:正常情况下</centor>

图2.png

<centor>图2:出问题时</centor>

然后回想起了..*.122:6390的TIME-WAIT已经超过了3W,会不会TIME_WAIT占用了太多端口导致端口不足呢。因此查看端口内核参数配置:

# sysctl -a | grep ip_local_port_range

net.ipv4.ip_local_port_range = 32768 65000

果然发现该机器上的端口范围只开了3W多个,也就是说端口已经几乎快用满了。那就提高端口可用数量:

# vim /etc/sysctl.conf
net.ipv4.ip_local_port_range = 10000 65000

connect系统调用恢复理性状态,整体服务器的CPU使用率非常健康。

问题的根本原因是建立TCP连接使用的端口数量上(ip_local_port_range)不充裕,导致connect系统调用开销上涨了将近100倍!

后来我们的一位开发同学帮忙翻到了connect系统调用里的一段源码

int inet_hash_connect(struct inet_timewait_death_row *death_row,

struct sock *sk)

{

return __inet_hash_connect(death_row, sk, inet_sk_port_offset(sk),

__inet_check_established, __inet_hash_nolisten);

}

int __inet_hash_connect(struct inet_timewait_death_row *death_row,

struct sock *sk, u32 port_offset,

int (*check_established)(struct inet_timewait_death_row *,

struct sock *, __u16, struct inet_timewait_sock **),

int (*hash)(struct sock *sk, struct inet_timewait_sock *twp))

{

struct inet_hashinfo *hinfo = death_row->hashinfo;

const unsigned short snum = inet_sk(sk)->inet_num;

struct inet_bind_hashbucket *head;

struct inet_bind_bucket *tb;

int ret;

struct net *net = sock_net(sk);

int twrefcnt = 1;

if (!snum) {

int i, remaining, low, high, port;

static u32 hint;

u32 offset = hint + port_offset;

struct inet_timewait_sock *tw = NULL;

inet_get_local_port_range(&low, &high);

remaining = (high - low) + 1;

local_bh_disable();

for (i = 1; i <= remaining; i++) {

port = low + (i + offset) % remaining;

if (inet_is_reserved_local_port(port))

continue;

......

}

}

static inline u32 inet_sk_port_offset(const struct sock *sk)

{

const struct inet_sock *inet = inet_sk(sk);

return secure_ipv4_port_ephemeral(inet->inet_rcv_saddr,

inet->inet_daddr,

inet->inet_dport);

}

从上面源代码可见,临时端口选择过程是生成一个随机数,利用随机数在ip_local_port_range范围内取值,如果取到的值在ip_local_reserved_ports范围内 ,那就再依次取下一个值,直到不在ip_local_reserved_ports范围内为止。原来临时端口竟然是随机撞。出。来。的。。 也就是说假如就有range里配置了5W个端口可以用,已经使用掉了49999个。那么新建立连接的时候,可能需要调用这个随机函数5W次才能撞到这个没用的端口身上。

所以请记得要保证你可用临时端口的充裕,避免你的connect系统调用进入SB模式。正常端口充足的时候,只需要22usec。但是一旦出现端口紧张,则一次系统调用耗时会上升到2.5ms,整整多出100倍。这个开销比正常tcp连接的建立吃掉的cpu时间(每个30usec左右)的开销要大的多。

解决TIME_WAIT的办法除了放宽端口数量限制外,还可以考虑设置net.ipv4.tcp_tw_recycle和net.ipv4.tcp_tw_reuse这两个参数,避免端口长时间保守地等待2MSL时间。

性能测试 | 服务器CPU使用率高分析实例的更多相关文章

  1. 服务器CPU使用率高的原因分析与解决办法

    我们的服务器在使用操作系统的时候,用着用着系统就变慢了,打开“ 任务管理器 ”一看,才发现CPU使用率达到80%以上.这是怎么回事情呢?遇到病毒了吗?硬件有问题?还是系统设置有问题呢?在本文中将从硬件 ...

  2. (原创)性能测试中,Oracle服务器定位CPU使用率高的瓶颈(SQL)

    本篇博客记录一次性能测试过程中,定位对CPU使用率高的瓶颈问题,主要定位SQL为准 一.用SQL命令定位1.首先用TOP命令监控系统资源,如果是AIX系统,就用topas,进入TOP命令的滚动刷新数据 ...

  3. 查询执行成本高(查询访问表数据行数多)而导致实例 CPU 使用率高是 MySQL 非常常见的问题

    MySQL CPU 使用率高的原因和解决方法_产品性能_常见问题_云数据库 RDS 版-阿里云 https://help.aliyun.com/knowledge_detail/51587.html ...

  4. 排查MongoDB CPU使用率高的问题

    1.公司业务调整,把一部分数据由Redis转至MongoDB,业务在测试环境正常,生产环境上线后发现压力一上来MongoDB的服务直接把CPU占满了,和开发的同学分析了一下也参考了一下百度上类似的问题 ...

  5. Java进程CPU使用率高排查

    Java进程CPU使用率高排查 生产java应用,CPU使用率一直很高,经常达到100%,通过以下步骤完美解决,分享一下.1.jps 获取Java进程的PID.2.jstack pid >> ...

  6. MySQL CPU 使用率高的原因和解决方法

    用户在使用 MySQL 实例时,会遇到 CPU 使用率过高甚至达到 100% 的情况.本文将介绍造成该状况的常见原因以及解决方法,并通过 CPU 使用率为 100% 的典型场景,来分析引起该状况的原因 ...

  7. 交换机CPU使用率高的原因

    交换机CPU的功能 1.管理已配置的软件协议,例如: – 生成树协议(STP) – 路由协议,例如OSPF和EIGRP – 热备路由协议(HSRP) – 思科发现协议(CDP) – 端口聚合协议(PA ...

  8. JVM进程cpu飙高分析

    在项目快速迭代中版本发布频繁  近期上线报错一个JVM导致服务器cpu飙高 但内存充足的原因现象.  对于耗内存的JVM程序来而言,  基本可以断定是线程僵死(死锁.死循环等)问题. 这里是纪录一下排 ...

  9. 线上服务器CPU彪高的调试方式

    原文内容来自于LZ(楼主)的印象笔记,如出现排版异常或图片丢失等问题,可查看当前链接:https://app.yinxiang.com/shard/s17/nl/19391737/2fee7b91-f ...

随机推荐

  1. 1 java 笔记

    第一java的版本: J2ME主要用于移动设备和信息家电 J2SE整个Java技术的核心 J2EE java技术应用最广泛的部分,主要应用与企业的开发 第二:基于java语言的开源框架 struts ...

  2. Java安全停止线程方法

    1. 早期Java提供java.lang.Thread类型包含了一些列的方法 start(), stop(), stop(Throwable) and suspend(), destroy() and ...

  3. 微信小程序html(wxml)传参

    欢迎加入前端交流群交流知识:749539640 习惯了vue.angular用微信小程序有时候真感觉非人类..需要用data-xxx 先说下我们在vue.angular里事件传参 //html < ...

  4. Mysql(九):Python连接MySQL数据库之pymysql模块使用

    Python3连接MySQL 本文介绍Python3连接MySQL的第三方库--PyMySQL的基本使用. PyMySQL介绍 PyMySQL 是在 Python3.x 版本中用于连接 MySQL 服 ...

  5. 用 Spark 处理复杂数据类型(Array、Map、JSON字符串等)

    split 对列表进行切割,然后生产新的列表 from pyspark.sql.functions import split# 对 key列按照 0-9之间的数字进行风格 重新命名为 s  df.se ...

  6. 关于微信小程序的本地存储

    微信小程序中会使用wx.setStorage(wx.setStorageSync)来存储数据,问题是:即使小程序被销毁了,本地缓存的数据仍然存在.会造成: 所以要及时清理掉本地缓存的数据.解决思路: ...

  7. luogu3651 展翅翱翔之时 (はばたきのとき)[基环树+贪心]

    考前随便做点水题愉♂悦身心 有助于退役 这题意思其实就是说要把外向基环树森林改成一个环的最小代价. 依照套路,先对每棵基环树的树做dp,这里因为要是环,要把所有的树都拆成链,然后连接.所以考虑以最小代 ...

  8. vue04 总结

    """ 1.环境 node:官网下载安装包,傻瓜式安装 - https://nodejs.org/zh-cn/ => 附带按照了npm cnpm:npm insta ...

  9. rsync 同步操作

    同步:增量拷贝,只传输变化过的数据 rsync   [ 选项]  源目录/目标目录 -a :归档模式  相当于 -rlptgoD -v:显示详细操作信息 -z:传输过程中启用压缩/解压 --delet ...

  10. 移动端H5开发问题记录

    1. 当弹出键盘时,会改变页面高度,影响页面样式 通过window.onsize事件可以控制键盘弹出或消失的时候的样式 var h = document.body.scrollHeight // 用o ...