内容简介:http keep-alive 相关资料非常多,如果深挖,那可能就长篇大论了,不合适普及,这只是一篇新手入门引导,主要讲解 node 下 http 请求的坑,以及 keep-alive 的简单实用,后续才会详细剖析原理。我司使用 node 做中间层开发,所以 api 都是 node 代理转发的,虽然目前 qps 不是特别高,都能满足需求,但压测总归是有的。
http keep-alive 相关资料非常多,如果深挖,那可能就长篇大论了,不合适普及,这只是一篇新手入门引导,
主要讲解 node 下 http 请求的坑,以及 keep-alive 的简单实用,后续才会详细剖析原理。
起因
我司使用 node 做中间层开发,所以 api 都是 node 代理转发的,虽然目前 qps 不是特别高,都能满足需求,但压测总归是有的。
不压不知道,一压,emmmmm。。。
搭建个纯净的 node 请求测试环境
先不说业务,我们来搭建个最简单的测试环境,看看压测 工具 能跑到多少 QPS。
然后我们写个 api 代理模块看看能跑到多少。
使用 node 官方例子 https://nodejs.org/en/about/ 。
const http = require('http'); const hostname = '127.0.0.1'; const port = 3000; const server = http.createServer((req, res) => { res.statusCode = 200; res.setHeader('Content-Type', 'text/plain'); res.end('Hello World\n'); }); server.listen(port, hostname, () => { console.log(`Server running at http://${hostname}:${port}/`); });
然后我们用 wrk 来压测,因为简单方便。
- 参数 -c 并发数,就想象成同时请求后台 api 的数量,一个页面往往会调用3-5个后台接口。
- 参数 -d 持续时间,这就是压测本质啊,持续越久,效果越真实,因为很多时候后面会挂掉的。
看看 10 并发,持续 10 秒的情况。
$ wrk -c 10 -d 10 http://localhost:3000 Running 10s test @ http://localhost:3000 2 threads and 10 connections Thread Stats Avg Stdev Max +/- Stdev Latency 523.02us 129.44us 4.04ms 92.33% Req/Sec 9.59k 550.11 10.55k 82.67% 192745 requests in 10.10s, 25.37MB read Requests/sec: 19083.81 Transfer/sec: 2.51MB
可以看出 Req/Sec 的 Avg 值,就是平均 QPS 为 9.59k,因为默认开了2线程,所以总 QPS 为 19k。
好了,测试环境和测试结果已经有了直观的展现。
下面搭建 node 接口代理然后重新压测。
node 接口代理
我们由于业务需求,没用 http-proxy 做直接的代理转发,而是基于 got 模块,自定义的接口模型。
我们先不考虑业务开销,直接代理转发看看结果。
const http = require('http'); const got = require('got'); const hostname = '127.0.0.1'; const port = 8000; const server = http.createServer((req, res) => { got.get('http://localhost:3000/').then(({ body }) => { res.statusCode = 200; res.setHeader('Content-Type', 'text/plain'); res.end(body); }).catch((err) => console.log(err)); }); server.listen(port, hostname, () => { console.log(`Server running at http://${hostname}:${port}/`); });
代码跟刚才一样,只是多了一层 got 代理接口。
这里没用 got stream 直接 pipe 给 res,业务我们的代理接口中会做其他业务操作。
现在来压测下这个服务的情况吧。
$ wrk -c 10 -d 10 http://localhost:8000 Running 10s test @ http://localhost:8000 2 threads and 10 connections Thread Stats Avg Stdev Max +/- Stdev Latency 13.63ms 20.29ms 193.78ms 94.45% Req/Sec 522.58 126.90 660.00 89.23% 10237 requests in 10.03s, 1.35MB read Requests/sec: 1020.52 Transfer/sec: 137.53KB
有点慌,什么情况,怎么会相差这么多。
我们在 3000 服务中加入 console.log(req.headers);
看看 headers 字段。
const server = http.createServer((req, res) => { console.log(req.headers); // 输出 headers res.statusCode = 200; res.setHeader('Content-Type', 'text/plain'); res.end('Hello World\n'); });
然后访问 8000 服务,看到控制台输出:
{ 'user-agent': 'got/9.3.2 (https://github.com/sindresorhus/got)', 'accept-encoding': 'gzip, deflate', host: 'localhost:3000', connection: 'close' }
其中 connection 所以每次请求都会重新建立 tcp 连接,浪费了不少性能。
接下来我们要打开 keep-alive 提升代理性能。
开启 keep-alive
安装 agentkeepalive
模块,这是 fengmk2 大佬封装的模块,我们直接使用看看效果先。
$ yarn add agentkeepalive
然后在 8000 服务中开启代理。
const http = require('http'); const got = require('got'); const Agent = require('agentkeepalive'); const agent = new Agent(); const hostname = '127.0.0.1'; const port = 8000; const server = http.createServer((req, res) => { got.get('http://localhost:3000/', { agent }).then(({ body }) => { res.statusCode = 200; res.setHeader('Content-Type', 'text/plain'); res.end(body); }).catch((err) => console.log(err)); }); server.listen(port, hostname, () => { console.log(`Server running at http://${hostname}:${port}/`); });
重新启动,访问 8000 服务,查看 3000 服务控制台。
{ 'user-agent': 'got/9.3.2 (https://github.com/sindresorhus/got)', 'accept-encoding': 'gzip, deflate', host: 'localhost:3000', connection: 'keep-alive' }
看到已经开启了 keep-alive,我们把 3000 服务中的 log 先关掉,否则影响结果。
$ wrk -c 10 -d 10 http://localhost:8000 Running 10s test @ http://localhost:8000 2 threads and 10 connections Thread Stats Avg Stdev Max +/- Stdev Latency 5.32ms 1.84ms 19.30ms 89.87% Req/Sec 0.96k 69.30 1.06k 78.50% 19141 requests in 10.02s, 2.52MB read Requests/sec: 1910.43 Transfer/sec: 257.46KB
可以看到性能提升了 1 倍,将近 1k 了,但跟原服务 9.59k 还是相差太多。
目前我们只能开多进程来提升整体 QPS 了。
比如开 9 进程,每个进程都能吃到近 1k,差不多可以吃满源服务。
但如果你开了 10 进程甚至更多,那瓶颈就会在源服务上,就需要给源服务加 cpu 或加服务器。
小结
这里还有个会影响结果的因素,而且影响会比较大。
因为是我本地测试,两个服务都跑本地,要严谨的话,应该跑在服务器上,或相同配置的虚拟机中。
不过本文意图是体验 keep-alive 和怎么用 node 吃满接口服务。
所以就不做那么严谨的测试了。
还要吐槽一点,node http 请求,性能真是低下啊,或者是我不知道怎么正确的使用。
因为 autocannon 压测工具,也是 node 写的,但他能吃满,我们直接写请求,只能达到 1/10。
我自己写了个基于 net 的 http 1.1 请求 demo 测试,只提升了100左右,并没有特别大的改进。
因为水平有限,没写基于 net + keep-alive 的 http 1.1 测试,所以不能下结论。
后续我会慢慢研究 keep-alive 然后记录分享的。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
你必须知道的495个C语言问题
Steve Summit / 孙云、朱群英 / 人民邮电出版社 / 2009-2 / 45.00元
“本书是Summit以及C FAQ在线列表的许多参与者多年心血的结晶,是C语言界最为珍贵的财富之一。我向所有C语言程序员推荐本书。” ——Francis Glassborow,著名C/C++专家,ACCU(C/C++用户协会)前主席 “本书清晰阐明了Kernighan与Ritchie《The C programming Language》一书中许多简略的地方,而且精彩地总结了C语言编程......一起来看看 《你必须知道的495个C语言问题》 这本书的介绍吧!