Nodejs长连接高并发问题,很有挑战,大家来讨论下

Nodejs长连接高并发问题,很有挑战,大家来讨论下

At the first time, I used “express” module, with which I could reach about 120k concurrent connections

express有这么强吗

18 回复

Node.js 长连接高并发问题,很有挑战,大家来讨论下

在现代Web应用中,处理高并发和长连接是一个常见的挑战。最近我尝试使用Express模块来处理这种情况,结果发现我可以达到大约12万的并发连接数。这让我感到非常惊讶,因为通常情况下,Express并不是专门设计用于处理这种级别的并发连接的。

为什么会出现这种情况?

首先,我们需要理解Express模块本身并不是为高并发而设计的。Express主要是作为一个轻量级的路由中间件,用于构建Web应用程序或API。然而,在某些情况下,它可能能够处理大量的并发连接,这取决于服务器硬件、网络配置以及代码优化等多种因素。

示例代码

以下是一个简单的Express应用示例,展示了如何设置一个基本的HTTP服务器:

const express = require('express');
const app = express();

app.get('/', (req, res) => {
    res.send('Hello World!');
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});

如何优化高并发和长连接?

  1. 使用集群模式:Node.js内置了cluster模块,可以用来创建多个工作进程来处理请求。这样可以充分利用多核CPU的能力。

    const cluster = require('cluster');
    const http = require('http');
    const numCPUs = require('os').cpus().length;
    
    if (cluster.isMaster) {
        console.log(`Master ${process.pid} is running`);
    
        // Fork workers.
        for (let i = 0; i < numCPUs; i++) {
            cluster.fork();
        }
    
        cluster.on('exit', (worker, code, signal) => {
            console.log(`Worker ${worker.process.pid} died`);
        });
    } else {
        // Workers can share any TCP connection
        // In this case it is an HTTP server
        http.createServer((req, res) => {
            res.writeHead(200);
            res.end('hello world\n');
        }).listen(8000);
    
        console.log(`Worker ${process.pid} started`);
    }
    
  2. 使用反向代理:Nginx或Apache等反向代理服务器可以在前端处理静态文件请求,并将动态请求转发给Node.js服务器,从而减轻Node.js服务器的压力。

  3. 异步编程:确保你的代码是高度异步的,避免阻塞操作,例如数据库查询或文件I/O操作。

  4. 使用更高效的中间件:一些专门为高并发设计的中间件,如fastifyhapi,可能会提供更好的性能。

  5. 负载均衡:通过在多个服务器之间分配请求,可以进一步提高系统的吞吐量和可靠性。

希望这些方法能帮助你更好地处理Node.js中的高并发和长连接问题。如果你有任何其他建议或经验分享,欢迎在评论区留言讨论!


120k这个也不是很准,因为我没有响应数据,后面测试发现开始相应请求后,每个连接会多占4k内存的样子,我猜跟socket send buffer有关,但是调整net.ipv4.tcp_wmem不起任何作用。另外express就是在原生http模块上封装了一些东西(比如在reqres上挂一些属性),肯定会多占一些内存,但是也不至于太多。

每人8K就是1000人在线需要8M,1W在线80M左右。

每人12KB万人在线也就消耗120M左右

很好奇LZ是什么史诗级应用, 顶级游戏服务器也不过为1W人左右并发连接设计

i5以上的CPU每核也就够NODE消耗1000个左右的并发响应, 超过这个数单是上下文切换就够它受的了,LZ还是先不要考虑内存的问题吧

纯粹用来推消息的应用,整个集群需要支撑千万级的用户量,所以单机并发量自然越高越好,可以省点机器。

实际上大部分连接大部分时候都会空闲,只占内存,所以我目前不是很担心CPU,想着先把内存占用优化到极致,再考虑推送消息的速率(CPU、网速等因素)。另外,也是好奇,极致能做到多少?在考虑这个问题的过程中也学到了不少东西,但是现在感觉卡住了。。。

i5以上的CPU每核也就够NODE消耗1000个左右的并发响应,超过这个数单是上下文切换就够它受的了

请问这个数据是怎么来得呢,node.js的上下文切换具体是指什么意思,谢谢!

nodejs是单线程的,不存在上下文切换,楼主的思路是对的,应该从消耗内存入手 ,我猜你目前要做的是网关服务器,只负责维护大量socket长链接,做消息转发,这样的话最好不要用express,它加那些东西没必要,另外,记得设置socket的两个属性,socket.setTimeout(0);socket.setNoDelay(true);这样内存消耗最少,实时性好,但是通信效率低一些,多个tcp packet不会组合成一个发送。如果真的是长链接,做游戏服务器,推荐ws,一个高效率的websocket实现,性能很好,我们也在用它做手游的服务端。

看到你发了代码? 另外发现这句: var res = new ServerResponse(socket);

完全没有必要用ServerResponse包装,你要写什么直接调用socket的write就好了,SR内部也会做buffer消耗你的内存。对了,kernel层面为你的每个socket消耗的内存你是很难优化了,默认的设置已经很合理,上面的socket.setTimeout(0);socket.setNoDelay(true);其实也是在设置该socket的内核handle参数了

你可以改变的是nodejs层面的东西,socket本身是继承自Stream接口的,Stream内存也有缓存,你想不用socket,直接使用handle也可以,在net模块里,self._handle.onconnection = onconnection;自己写onconnection,这个回调里每个连接进来你基本上就是得到一个socket handle,也就是对kernel socket handle的包装,直接用它好了

如果你还想再进一步,算了 哥们儿 你放弃nodejs,自己用c吧,用libev+mmap ,再进一步,你还可以写kernel driver,直接读写网卡硬件端口,再进一步。。。哥已经帮不了你了。。。

有意义嘛?

的确是类似网关的东西

socket默认就是没有超时和setNoDelay的

ws不太了解,一会看一下,谢谢!

ServerResponse这个是我自己定义的,里面封装了http响应头Transfer-Encoding: chuned的处理,这样做是为了跟原生http模块保持一致。

直接使用handle

我以为在node.js层面net.Socket已经够底层了,看来不是,你说得方法值得一试。

自己用c吧,用libev+mmap

这个有空绝对要做一下看看

kernel driver,直接读写网卡硬件端口

这个就有点太底层了……目前没这个能力

有意义吗?

当然,可以了解到不少底层的东西。。。

谢谢你的建议!

v8是单线程的,不代表NODE就是单线程的,之所以NODE不是单线程的是因为其各种中间件模块的运作不是单线程的,例如MYSQL BINDING;当然会存在上下文切换

var rep = '高手甚多呀';

看了一下net.Socket的实现,觉得这个地方没办法省了,它上面挂的属性也不多,多数是方法,直接用handle的话差不多也要处理那么多东西。。。

node.js的单线程是指所有用户代码是在单线程中执行的是吧?我用pstree看node.js进程,显示这个:

node───2*[{node}]

这应该是两个线程吧?

想问一下,压测长连接的并发量,一般通过什么方法压比较靠谱?

我都是用node.js根据业务场景自己模拟客户端来测,长连接不像http,http是标准的,有很多开源工具(ab、siege)可以用。

楼主你好,我们做的应用和你这个类似,是连接网关的一个东西。目前我们是用java nio来实现的,我想问的是如果换成node来实现是不是代码会更简单,效率能更高些。

纯粹比效率java比node.js还是要高点的,不过java的gc比较难调,设得不好来次gc会有很长的延时。

用netty的话每个连接要占50k左右的内存。

我们做mqtt的长连接每个连接也要10k以上,用socket.io要更多,不过现在内存便宜, 觉得没必要做优化。 从实用的角度这样已经够用了, 不过研究一下底层确实可以学到不少东西,期待楼主的成果。

LZ 菜鸟请教一个问题,怎么看一个socket连接占用内存数? 用工具?

关于 Node.js 的长连接和高并发问题,确实是一个具有挑战性的主题。Node.js 本身是单线程的事件驱动模型,这使得它在处理高并发请求时表现出色。但是,当你需要处理长连接(如 WebSocket 或 HTTP 长轮询)时,情况可能会变得更加复杂。

示例代码:使用 ws 模块实现 WebSocket 长连接

const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', function connection(ws) {
    console.log('A client connected!');

    ws.on('message', function incoming(message) {
        console.log('received: %s', message);
        // 处理接收到的消息
    });

    ws.on('close', function close() {
        console.log('A client disconnected!');
    });
});

console.log('WebSocket server is running on ws://localhost:8080');

如何优化高并发场景

  1. 集群模式:使用 Node.js 的 cluster 模块来利用多核 CPU。这样可以将负载分发到多个子进程中。

    const cluster = require('cluster');
    const http = require('http');
    const numCPUs = require('os').cpus().length;
    
    if (cluster.isMaster) {
      for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
      }
    } else {
      const WebSocket = require('ws');
      const wss = new WebSocket.Server({ port: 8080 });
    
      wss.on('connection', function connection(ws) {
          // ...
      });
    }
    
  2. 连接池:对于数据库操作,可以使用连接池来管理数据库连接,以避免频繁创建和销毁连接。

  3. 性能调优:通过使用 perf_hooks 模块进行性能分析,找出瓶颈并进行优化。

  4. 异步处理:确保所有的 I/O 操作都是异步的,避免阻塞事件循环。

  5. 负载均衡:使用反向代理服务器(如 Nginx 或 HAProxy)来进行负载均衡,以进一步分散请求压力。

这些策略可以帮助你更好地应对 Node.js 中的长连接和高并发问题。希望这些建议对你有所帮助!

回到顶部