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 然后记录分享的。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Game Engine Architecture, Second Edition

Game Engine Architecture, Second Edition

Jason Gregory / A K Peters/CRC Press / 2014-8-15 / USD 69.95

A 2010 CHOICE outstanding academic title, this updated book covers the theory and practice of game engine software development. It explains practical concepts and techniques used by real game studios,......一起来看看 《Game Engine Architecture, Second Edition》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具