关于Nodejs能同时接受多少个请求的问题?

关于Nodejs能同时接受多少个请求的问题?

最近学习node,看了很多教程,都在赞扬nodejs的异步I/O,异步I/O的特点就是,每接收一个请求,使用异步调用处理请求,不用等待结果,可以继续运行其他操作,也就是说可以继续接受请求。那它到底能接受?于是,我写了个程序来测试,代码如下:

// server.js
// 处理一个请求需要5秒,通过setTimeout设置5秒后响应
var http = require("http");
var n = 0;
http.createServer(function (req, res) {
    n++;
    setTimeout(function() {
        console.log("Accept " + n + "request.");
        res.end("test");  
    }, 5000);
}).listen(3000);
// client.js

// 发送一个请求 exports.send = function () { var http = require(‘http’); var options = { host: ‘localhost’, port: ‘3000’, path: ‘/’, method: ‘GET’, };

var req = http.request(options, function(res){
    exports.counter += 1;
    res.setEncoding('utf8');
    res.on('data', function (c) {
        //console.log(c);
    });
    res.on('end', function() {
        exports.seccess += 1;
        console.log("Response: " + exports.seccess);
    });
});
req.end();

};

exports.seccess = 0;

// attack.js
// 在1秒内发出约50000个请求
var client = require(’./client’);

var d = 1000, t = Date.now(); while(Date.now() - t < d) { client.send(); } console.log(‘end.’);

运行server.js

> node server.js

运行atack.js发起请求,(经测试每秒能发出约50000个请求):

> node atack.js

结果:

Accept 5request.
Accept 5request.
Accept 5request.
Accept 5request.
Accept 5request.
Accept 10request.
Accept 10request.
Accept 10request.
Accept 10request.
Accept 10request.
Accept 15request.
Accept 15request.
Accept 15request.
Accept 15request.
Accept 15request.
// 省略后面N条

通过结果可以知道server.js在5秒内只接受了5个请求,这是为什么呢?


8 回复

在Node.js中,其单线程事件循环模型允许它高效地处理大量的并发请求。但是,这并不意味着它可以同时处理无限数量的请求。Node.js的性能受到多个因素的影响,包括系统的硬件资源、网络带宽以及应用逻辑的复杂性。

你提到的测试结果显示,在5秒内只接受了5个请求,这是因为Node.js的事件循环模型和JavaScript引擎的限制。尽管Node.js是非阻塞的,并且可以处理大量的并发请求,但它仍然依赖于单线程事件循环。这意味着在任何给定的时间点,只有一个操作在执行。如果一个操作(如处理数据库查询或文件I/O)耗时较长,那么事件循环将被阻塞,直到该操作完成。

示例代码解析

server.js

// server.js
// 处理一个请求需要5秒,通过setTimeout模拟长任务
var http = require("http");
var n = 0;

http.createServer(function (req, res) {
    n++;
    setTimeout(function() {
        console.log("Accept " + n + " request.");
        res.end("test");
    }, 5000); // 模拟5秒的处理时间
}).listen(3000);

在这个服务器端代码中,每次接收到一个请求,都会启动一个定时器,模拟5秒的处理时间。由于Node.js是单线程的,所以在这5秒内,事件循环无法处理新的请求。

client.js

// client.js
// 发送一个请求
exports.send = function () {
    var http = require('http');
    var options = {
        host: 'localhost',
        port: '3000',
        path: '/',
        method: 'GET',
    };

    var req = http.request(options, function(res){
        exports.counter += 1;
        res.setEncoding('utf8');
        res.on('data', function (chunk) {
            // 处理数据
        });
        res.on('end', function() {
            exports.success += 1;
            console.log("Response: " + exports.success);
        });
    });
    req.end();
};

exports.success = 0;

这个客户端代码用于发送HTTP请求到服务器。它通过http.request方法向服务器发送请求,并监听响应。

attack.js

// attack.js
// 在1秒内发出约50000个请求
var client = require('./client');

var d = 1000,
    t = Date.now();
while(Date.now() - t < d) {
    client.send();
}
console.log('end.');

这个脚本在一个循环中快速地发送请求,试图在1秒内发送大约50000个请求。

结果分析

当你运行这些脚本时,你可能会发现服务器端只接受了一小部分请求,因为Node.js的事件循环被阻塞了。每个请求都需要5秒才能得到响应,因此在5秒内只能处理有限数量的请求。

如何优化

为了更好地利用Node.js的非阻塞性质,你可以考虑以下几点:

  1. 使用集群模块:Node.js提供了一个内置的cluster模块,可以在多核CPU上创建子进程,从而实现真正的并行处理。
  2. 优化I/O操作:确保你的I/O操作尽可能快地完成,例如使用缓存或优化数据库查询。
  3. 使用异步库:使用如asyncbluebird等库来管理异步操作,以提高代码的可读性和性能。

通过这些优化措施,你可以显著提高Node.js应用程序的性能和并发处理能力。


或者大家有什么办法可以测试这个问题呢?

哈哈哈哈哈。Node.js 的 socket 模块的一个限制导致的,好像是同一 IP 不能连接超过 5 个 socket。

我找找资料看。

http://nodejs.org/api/http.html#http_agent_maxsockets

对于同一个来源,比如 127.0.0.1:23423,Node.js 默认只接受最多 5 个请求。

去提高这个限制就好了。

一般设置多少合适呢? 每秒5个会不会太少? 如果遇到很多资源文件 图片的话…

最后我把server.js的setTimeout的时间改成了100ms, maxSocket为2000大概返回 accept 1000个request 左右,也就是说100ms内接收了1000个request。

结论:所以如果不考虑其他因素,单谈接收的request的速率有10000/s。当然这个结果是很片面的,客户端和服务端在同一台电脑,两者的socket I/O的读写会相互影响结果(如我在调节maxSockets的值时,这个值越大,结果server的处理速度会越小)。

server的处理速度会越小??

还是 server的平均处理速度会越小?

Node.js 是一个基于事件驱动的非阻塞 I/O 模型的运行环境,理论上它可以同时处理大量的并发请求。然而,实际能够同时处理的请求数量受多种因素影响,包括但不限于服务器硬件资源、操作系统限制、网络带宽以及 Node.js 进程的配置等。

你观察到的结果(即5秒内只接受了5个请求)可能是因为你的测试环境中存在一些限制条件,例如操作系统的文件描述符数量限制、Node.js 的事件循环线程池大小等。在默认情况下,Node.js 的单线程事件循环可能会成为瓶颈,尤其是在 CPU 密集型任务中。

为了更好地理解这一点,你可以尝试修改 server.jsattack.js 中的代码,以便更清楚地看到 Node.js 的并发能力。这里提供一个简单的示例:

示例代码

server.js

var http = require('http');
http.createServer(function (req, res) {
    setTimeout(function() {
        res.end("test");
    }, 5000);
}).listen(3000);

attack.js

var http = require('http');
var options = {
    host: 'localhost',
    port: '3000',
    path: '/',
    method: 'GET',
};
var start = Date.now();

for (let i = 0; i < 50000; i++) {
    http.get(options, function(res) {
        res.on('data', function(chunk) {});
        res.on('end', function() {
            if ((i + 1) % 1000 === 0) {
                console.log(`Processed ${i + 1} requests. Elapsed time: ${Date.now() - start}ms`);
            }
        });
    }).on('error', function(e) {
        console.error(`Got error: ${e.message}`);
    });
}

解释

  1. server.js:创建了一个 HTTP 服务器,每次请求会延迟5秒才响应。
  2. attack.js:在1秒内发送了大约50000个请求,并记录了处理每个请求的时间。

通过上述代码,你可以看到 Node.js 如何处理大量并发请求。如果你发现处理速度较慢或请求未能被正确处理,可能是由于以下原因:

  • 系统资源限制:如文件描述符数量、内存限制等。
  • CPU 限制:如果请求处理涉及大量的计算,可能会导致 CPU 占用率过高,从而限制并发请求数量。

为了提高并发性能,你可以考虑使用集群模式或 worker threads 来利用多核 CPU,或者优化业务逻辑以减少 CPU 占用。

回到顶部