node处理高并发请求cpu过高的问题
发布于 2 个月前 作者 sunstdot 1950 次浏览 来自 问答

问题描述: 在公司有做一个node中间层负责 merge多个后端接口返回给前端调用,现在有一情景,需要分7次请求某个后端接口,一次请求6个id,这7次可以并发请求, 单纯压测后端接口(6个id的情况),qps可达7000,node中间层代理后 qps降到了 220左右, 去掉了其他影响条件,只是单纯的并发请求7次接口,qps也只有280左右, cpu占用率 高达80%,内存占用不高,应该只有socket创建产生的内存,几十M左右。 使用strace 跟踪进程,发现是 image.png futex跟读写操作占cpu使用较高 使用pstack分析结果: image.png C++较弱,看样子是在创建序列化JSON对象。

问题条件: 1.打印这7次并发请求的时间,发现最后一次请求的时间 远大于前6次,应该是响应时间过长导致qps上不去, 做了以下优化:agent的maxSocket设置为3000,keepalive为true,减少tcp握手浪费的时间,使用tcpdump抓包,确实没有了tcp握手这一步,时间有一点点减少

  1. 单个接口(批量6个id请求后端接口)时间为 20ms左右,但是并发七个 请求 就变成了 1500ms+了 (忽略该条件,重新验证时间差不多)

令人头秃的疑问: socket在资源足够的情况下理论上是可以无限创建的,这意味着request请求可以无限发起,而且node本身是异步的,网络io不需要阻塞等待,可以继续发起下一次req,如果下一个事件循环中res返回了,再进行处理。 即使是由于node事件监听这块的性能有损耗,不能无限发起,现在的情况 也太低了, 应该到1000qps左右才算正常,(参照后端接口7000的并发)

希望cnode 里的node大佬给咱解释一下这个问题。 以上测试 都是在单纯的并发7个请求并返回给前端的情况,中间层无额外处理逻辑。 拜谢~~


问题补充: 框架使用的是简化版的egg,基于koa的,请求包用的是request,并未对 http请求做额外的封装 补充一: 重新验证了同时发6次,7次的情况,日志里记录的时间 差不多,没有较大差异, 使用process.hrttime()记录的,应该比较准

代码补充二: image.png

一直在测试,把代码搞得比较丑陋,大家将就看, 1,2 分别是切割出 长度为3和4的数组,每个数组的item是 6个视频id组成的字符串,我要去后端请求这6个id的数据 下面的promiseAll是请求接口的操作,promise.all 里面 就是request请求,没做任何其他的事情 然后把数据返回。 以上

补充三 论坛里应该不止我们一家 把node 用在生产环境做中间层, 大家应该也会遇到这样的问题,还请 大佬指教 或者说一下自己的使用经验, 是不是 node本身的性能就这样? 我觉得还是蛮值得探讨一下的

问题关闭结论: 真是很尴尬啊,测试同学的压测集群 跟我们测试机以及后端接口不是一个 运营商,属于跨机房调用了, 当时说的7000qps的压测数据 是 测试同学压测 后端同机房同运营商接口 的数据, 压测我们中间层接口的时候,变成夸机房了,所以 这个7000qps是不准的, 刚才在测试集群上 压 跨机房的 后端接口 qps大概3800左右,我在我的测试机上ab测试压 后端接口也是3800,在测试机上压本机接口127,0,0,1这个 qps 为430左右,乘以7算下来与后端的接口qps大致相当,所以node做接口转发是没问题的,node的性能以及http socket以及time_wait并不是本次的性能瓶颈。 错的是我。 幸好及时发现了这个问题,还是感谢社群大佬的解答,这个问题暂时不删了,希望能给犯了相同错误的同学一点警示。 拜谢~~~

29 回复

都是请求同一个后端服务吗? TCP有个TIME_WAIT的梗

@vincent178 对,都是同一个后端服务,但是要调七次,TIME_WAIT 这个当时也查了,在linux系统文件里做了配置,但是貌似没起作用,所以用了http长连接,不关闭tcp继续复用它、

如果改成调用6次呢?是否还是最后一次请求时间比较长

@waitingsong 刚重新测试了一下,每次请求的时间 大致差不多,当时应该是 某些不可知的网络原因吧,骚瑞,我补充到问题中

单个接口(批量6个id请求后端接口)时间为 20ms左右,但是并发七个 请求 就变成了 1500ms+了

这不就是后端响应太慢了导致 qps 低的么

pstack 看到的是当前的运行栈吧,要看一段时间内的统计可以做一个 cpu profile 看看,可以接入使用下 Node.js 性能平台

@hyj1991 这个条件 重新验证了,第七个响应时间没有明显增长, 但是并发七个的时候 响应时间 的确变长了。 两个问题

  1. 我并发去请求接口,按理说总响应时间应该是我最长的那个接口的响应时间,现在并不是这样,举个例子我接口是80,90ms,总响应时间可能是210ms。(日志里刚看的时间数据)
  2. 后端响应慢了会导致qps低的, 但是 node服务是在不停地往后端发请求, 即使响应变低了比如1s响应,那么1s后我当前时间发送的请求就应该响应回来,此时qps应该恢复到正常才对。。。。这块儿有点绕不过来了,求解释

之所以没贴代码,是因为这个问题代码层面没做什么东西,就是一个转发请求+ 并发请求后端接口 遇到的qps 过低的问题

@hyj1991 是的,多次执行这个命令,看出现最多的那个 ,我本地有接这个easy-monitor, 线上测试机 也接入一下,还是很吊的工具,大佬流弊

@sunstdot 接口 80,90ms,是你单独测试的结果么,有没有可能就是并发请求下,后端的响应就会慢下来,那链路的 qps 自然上不去; 这一块正常情况下不太可能,如果后端和你说的一样的话,并发就是取最大的那一次,100ms 附近是正常的,我觉得要么是后端在并发接收不同接口的时候性能就是下降了,要么就是你的并发请求代码写的有问题。 另外请求在后端如果有积压,高并发下会出现雪崩,qps 上不来的。

@sunstdot 还有我前面发的是 Node.js 性能平台哦,不是 easy-monitor,这个免费工具功能会更强大一些

@hyj1991 已经把其他干扰因素降到最低了,并发请求用的都是同一个接口,参数都不变, 请求完直接返回给前端,不做任何处理,使用的是primise.all 测试同学、我自己分别压测了后端接口,qps能到7000, 压测我的接口 qps 只有不到300左右,算下来 后端接口 也才2000+的qps ,应该不会积压他们引发雪崩

压测的经过是 我的接口(并发请求7次后端接口)的响应时间 确实要比 压测后端接口 的响应时间 大上200ms左右,不管是 avg,min还是max 疑惑的是node 接口的表现不该是这样差

@sunstdot 最大文件打开数这些常规的 linux 值设置了么,还有可以升级下 node 到最新的 lts,转发性能应该 ok 的

@hyj1991 这个明天我去公司了看看,在运维那申请的虚机,应该是统一配置的,也升级一下node看看。我觉得node的性能不应该会那么差,但是我这边测试环境下基本上都删干净了就剩转发了…… 明天再试试,不行只能考虑缓存或者加机器了

来自酷炫的 CNodeMD

要不稍微贴贴代码?

贴下代码会比较好,怎么写并发7次请求的?7次都是公用一个socket吗

@vincent178 已经贴了,不一定是共用一个socket,我看node 源码里, _http_agent addrequest 操作里,是维护一个socket队列的,共用这个socket队列里的socket

@aojiaotage 已经贴了,放到补充里了,因为是公司代码,不能贴的太全, 还请见谅哈

@vincent178 在业务层没有对socket这块做什么事情,毕竟中间还隔了一个request npm包。

@sunstdot getBaseVideoInfo 那块,你request怎么用的?

@vincent178 image.png image.png

截图一是对http.agent的设置,截图二是具体的使用request

问题找到了,是我们自己的问题,已补充到问题描述中, 感谢各位大佬的解答~ 抱歉

@sunstdot 问题解决了就好,都是血泪经验

@vincent178 其实问题严格来说并没有解决,只是解除了对node的怀疑, 性能优化这块还有很长的路要走。 毕竟我们node中间层 是在做接口聚合跟解耦前端数据操作的事情,如果在merge多个接口的时候 qps一直上不去,会严重限制它的使用场景。 下一步研究一下redies缓存,在中间层跟后端这块儿加一层缓存。

@sunstdot 应该在同机房部署你的中间件,这样qps就上去了

来自酷炫的 CNodeMD

@vincent178 后端接口的部署情况略复杂, 负载均衡机器跟 业务机器 都不在一个机房, 每个负载均衡机器下 的 业务机数量也不同,不好推进,再试试推一下吧

看来遇到问题首先用抓包来分析还是有必要的。。。。

@sunstdot 请教一下,跨机房只能说明延迟增加了,为什么qps也会降低呢?是因为链接数增多导致的吗。

所以说这个是受机房之间的网络影响?

回到顶部