node http keep-alive demo

栏目: 后端 · 前端 · 发布时间: 5年前

内容简介: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语言问题

你必须知道的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语言问题》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

随机密码生成器
随机密码生成器

多种字符组合密码

URL 编码/解码
URL 编码/解码

URL 编码/解码