nodejs压力测试
背景
随着公司的业务的扩展,并发也越来越高,随着并发的变高,必然会带来很多问题,比如响应变慢,响应超时,甚至服务宕机,为了能够更好的处理此类问题,我们必须了解nodejs的基础性能,下面就是针对以下两种情况做的压力测试。
-
nodejs每秒钟能处理多少个简单请求
-
nodejs每秒中能够处理多少个简单查询数据库的请求
准备条件
- server: 原生nodejs搭建一个http服务,每个请求返回1k的数据, node version:10
const http = require('http');
let port = process.env.port | 3000
http.createServer(app).listen(port, () => {
console.log(`server is listening ${port}`)
})
let num = 0;
function app(req, res) {
console.log(`第${num++}次访问`)
res.end(getdata().toString())
}
function getdata() {
let size = 1 * 1024
let arr = new Array(size)
for (let i = 0; i < arr.length; i++) {
arr[i] = 1
}
return arr;
}
-
ab压力测试工具
-
硬件条件
阿里云 Ubuntu,8G,2CPU
测试场景一
请求10000次,并发分别为10 100 1000 5000 10000
ab -n10000 -c10 http://localhost:3000/
Concurrency Level: 10
Time taken for tests: 1.763 seconds
Complete requests: 10000
Failed requests: 0
Total transferred: 21220000 bytes
HTML transferred: 20470000 bytes
Requests per second: 5672.22 [#/sec] (mean)
Time per request: 1.763 [ms] (mean)
Time per request: 0.176 [ms] (mean, across all concurrent requests)
Transfer rate: 11754.35 [Kbytes/sec] received
ab -n10000 -c100 http://localhost:3000/
Concurrency Level: 100
Time taken for tests: 1.338 seconds
Complete requests: 10000
Failed requests: 0
Total transferred: 21220000 bytes
HTML transferred: 20470000 bytes
Requests per second: 7472.81 [#/sec] (mean)
Time per request: 13.382 [ms] (mean)
Time per request: 0.134 [ms] (mean, across all concurrent requests)
Transfer rate: 15485.66 [Kbytes/sec] received
ab -n10000 -c1000 http://localhost:3000/
Concurrency Level: 1000
Time taken for tests: 1.489 seconds
Complete requests: 10000
Failed requests: 0
Total transferred: 21220000 bytes
HTML transferred: 20470000 bytes
Requests per second: 6713.89 [#/sec] (mean)
Time per request: 148.945 [ms] (mean)
Time per request: 0.149 [ms] (mean, across all concurrent requests)
Transfer rate: 13912.97 [Kbytes/sec] received
ab -n10000 -c5000 http://localhost:3000/
Concurrency Level: 5000
Time taken for tests: 2.179 seconds
Complete requests: 10000
Failed requests: 0
Total transferred: 21220000 bytes
HTML transferred: 20470000 bytes
Requests per second: 4589.91 [#/sec] (mean)
Time per request: 1089.345 [ms] (mean)
Time per request: 0.218 [ms] (mean, across all concurrent requests)
Transfer rate: 9511.52 [Kbytes/sec] received
ab -n10000 -c10000 http://localhost:3000/
Concurrency Level: 10000
Time taken for tests: 1.870 seconds
Complete requests: 10000
Failed requests: 0
Total transferred: 21220000 bytes
HTML transferred: 20470000 bytes
Requests per second: 5348.45 [#/sec] (mean)
Time per request: 1869.701 [ms] (mean)
Time per request: 0.187 [ms] (mean, across all concurrent requests)
Transfer rate: 11083.41 [Kbytes/sec] received
并发 | 总花费时间/s | 失败个数 | 每秒请求(TPS) | 总传输 | 每个请求/ms | 传输速率(k/s) |
---|---|---|---|---|---|---|
10 | 1.763 | 0 | 5672 | 20M | 0.176 | 11754 |
100 | 0.873 | 0 | 7472 | 20M | 0.134 | 15485 |
1000 | 0.866 | 0 | 6713 | 20M | 0.149 | 13912 |
5000 | 1.381 | 0 | 4589 | 20M | 0.218 | 9511 |
10000 | 1.495 | 0 | 5348 | 20M | 0.187 | 11083 |
总结
在本地环境,基本不考虑网络因素,不考虑每个请求返回1KB数据,TPS能够达到7000~1100, 并不是并发越大请求越快,两则之间不存在线性关系
测试场景二
基于上述程序,添加连接mongodb,每次查询大小为1kb的数据,返回,测试
const http = require('http');
let port = process.env.port | 3000
http.createServer(app).listen(port, () => {
console.log(`server is listening ${port}`)
})
let num = 0;
function app(req, res) {
console.log(`第${num++}次访问`)
getDataFromDb(res)
}
function getdata() {
let size = 1 * 1024
let arr = new Array(size)
for (let i = 0; i < arr.length; i++) {
arr[i] = 1
}
return arr;
}
function getDataFromDb(res){
let data = Blog.findOne({},(err,result) => {
res.end(result.toString())
})
}
const mongoose = require("mongoose")
mongoose.connect('mongodb://localhost:30001/tim', {useNewUrlParser: true});
const Schema = mongoose.Schema;
const blogSchema = new Schema({
title: String,
body: []
});
const Blog = mongoose.model('Blog', blogSchema);
let obj = {title:"tim",body:getdata()}
const post = new Blog(obj);
post.save()
请求10000次,并发分别为10 100 150
ab -n10000 -c10 http://localhost:3000/
Concurrency Level: 10
Time taken for tests: 12.923 seconds
Complete requests: 10000
Failed requests: 0
Total transferred: 11690000 bytes
HTML transferred: 10940000 bytes
Requests per second: 773.84 [#/sec] (mean)
Time per request: 12.923 [ms] (mean)
Time per request: 1.292 [ms] (mean, across all concurrent requests)
Transfer rate: 883.41 [Kbytes/sec] received
ab -n10000 -c100 http://localhost:3000/
Concurrency Level: 100
Time taken for tests: 11.268 seconds
Complete requests: 10000
Failed requests: 0
Total transferred: 11690000 bytes
HTML transferred: 10940000 bytes
Requests per second: 887.44 [#/sec] (mean)
Time per request: 112.684 [ms] (mean)
Time per request: 1.127 [ms] (mean, across all concurrent requests)
Transfer rate: 1013.10 [Kbytes/sec] received
ab -n10000 -c1000 http://localhost:3000/
Concurrency Level: 1000
Time taken for tests: 12.815 seconds
Complete requests: 10000
Failed requests: 0
Total transferred: 11690000 bytes
HTML transferred: 10940000 bytes
Requests per second: 780.33 [#/sec] (mean)
Time per request: 1281.508 [ms] (mean)
Time per request: 1.282 [ms] (mean, across all concurrent requests)
Transfer rate: 890.83 [Kbytes/sec] received
ab -n10000 -c5000 http://localhost:3000/
Concurrency Level: 5000
Time taken for tests: 19.061 seconds
Complete requests: 10000
Failed requests: 0
Total transferred: 11690000 bytes
HTML transferred: 10940000 bytes
Requests per second: 524.63 [#/sec] (mean)
Time per request: 9530.570 [ms] (mean)
Time per request: 1.906 [ms] (mean, across all concurrent requests)
Transfer rate: 598.92 [Kbytes/sec] received
```ab -n10000 -c10000 http://localhost:3000/``
Concurrency Level: 10000
Time taken for tests: 12.900 seconds
Complete requests: 10000
Failed requests: 0
Total transferred: 11690000 bytes
HTML transferred: 10940000 bytes
Requests per second: 775.19 [#/sec] (mean)
Time per request: 12900.048 [ms] (mean)
Time per request: 1.290 [ms] (mean, across all concurrent requests)
Transfer rate: 884.96 [Kbytes/sec] received
并发 | 总花费时间/s | 失败个数 | 每秒请求(TPS) | 总传输 | 每个请求/ms | 传输速率(k/s) |
---|---|---|---|---|---|---|
10 | 12.923 | 0 | 773.84 | 11M | 1.292 | 883.41 |
100 | 11.268 | 0 | 887.44 | 11M | 1.127 | 1013 |
1000 | 12.815 | 0 | 780.33 | 11M | 1.282 | 890 |
5000 | 19.061 | 0 | 524.63 | 11M | 1.906 | 598 |
10000 | 12.900 | 0 | 775.19 | 11M | 1.290 | 884 |
总结
当有数据库操作时TPS在500~800左右,也就是平时我们的代码性能很好的情况下,如果存在数据库操作,单个实例TPS在500~800左右, 并发对TPS有较小影响
测试5000并发过程发生错误 apr_socket_recv: Connection reset by peer (104)
内核会认为系统受到了SYN flood攻击,会发送cookies(possible SYN flooding on port 80. Sending cookies),这样会减慢影响请求的速度,所以在应用服务武器上设置下这个参数为0禁用系统保护就可以进行大并发测试了:
# vim /etc/sysctl.conf
net.ipv4.tcp_syncookies = 0
# sysctl -p
然后就可以超过5000个并发测试了
Notes 如果高并发出现错误Apache Bench test error on OS X: “apr_socket_recv: Connection reset by peer (54)”,请参考这篇文章 ,如果更新过程出现错误 configure: error: APR not found . Please read the documentation,请参考这篇文章
node的性能远不止这么点,socket会消耗文件句柄,每个连接会消耗一些内存,所以你需要研究怎么调整内核参数,还有你如果在同一台机器上跑服务和测试,客户端会消耗端口号,(服务端只需要一个端口,而每个客户端连接都会占用一个端口,我有一次去面试,面试官居然和我争论这个问题,我很坚定的对他说,我敢100%确信你没有做过高并发项目)
@zengming00 在不同的机器上会增加一个变量,网络传输,这个点你怎么看
@zengming00 文件句柄是65535,应该不是瓶颈
node.js 升级到 12 ,数据库放在别的内网服务器。
@zuohuadong 这个建议挺好
没有对比就没有伤害,你可以试试golang
@ganshiqingyuan 之前对比过 fastify 和 gin ,不过跑的 helloworld ,15% 差距~
@ganshiqingyuan 我用斐波那契函数测试了以下,大概是45次 node:go 12:4.7左右
@TimLiu1 node 单线程,适合io密集型,,计算型的肯定比不上golang,,直接读文件node应该会好点,当然还是比不过golang多核一起干
@ganshiqingyuan 读文件的时候是调用系统的文件读取系统,是多线程操作,node只负责监听读取完成之后的事件和数据
用node 的express 框架写的服务端,现扫描出Slow HTTP Denial of Service Attack漏洞,要怎么弄??