Nodejs socket.io 高并发实战

Nodejs socket.io 高并发实战

socket.io实战

nodejs mult process + nginx + redis + socket.io 实现c50K,目前正在优化app,希望能实现c100K. 目前 c50K 非常稳定。c100K 下 会有写 400 和 502 错误,以及个别进程 cpu 100%.

通过 v8 自带的 prof 监控的日志:

ticks  total  nonlib   name
11453687   94.9%    0.0%  /lib64/libc-2.12.so
327343    2.7%    0.0%  /usr/bin/node
32393    0.3%    0.0%  /lib64/libpthread-2.12.so
1189    0.0%    0.0%  7fff269c3000-7fff269c4000
996    0.0%    0.0%  /lib64/libm-2.12.so
947    0.0%    0.0%  /usr/lib64/libstdc++.so.6.0.13
475    0.0%    0.0%  /lib64/librt-2.12.so
391    0.0%    0.0%  ffffffffff600000-ffffffffff601000
  1    0.0%    0.0%  /lib64/ld-2.12.so

JavaScript

ticks  total  nonlib   name
34551    0.3%   13.9%  LazyCompile: *exports._unrefActive timers.js:425
 9061    0.1%    3.6%  Stub: CompareStub_EQ
6462    0.1%    2.6%  LazyCompile: EventEmitter.emit events.js:53
4519    0.0%    1.8%  LazyCompile: EventEmitter.addListener events.js:126
3929    0.0%    1.6%  KeyedLoadIC: A keyed load IC from the snapshot
3818    0.0%    1.5%  LazyCompile: ~onread net.js:496
3817    0.0%    1.5%  LazyCompile: *remove _linklist.js:47
3117    0.0%    1.3%  LazyCompile: *append _linklist.js:63
2857    0.0%    1.1%  LazyCompile: *writeOrBuffer _stream_writable.js:200
2653    0.0%    1.1%  Builtin: A builtin from the snapshot {5}
2590    0.0%    1.0%  Stub: CEntryStub
2242    0.0%    0.9%  LazyCompile: *EventEmitter events.js:26
2171    0.0%    0.9%  LazyCompile: EventEmitter.removeListener events.js:191
2047    0.0%    0.8%  Builtin: A builtin from the snapshot
1976    0.0%    0.8%  Stub: FastNewClosureStub
1971    0.0%    0.8%  LazyCompile: *Url.parse url.js:105
1824    0.0%    0.7%  LazyCompile: *Buffer buffer.js:156
1763    0.0%    0.7%  Stub: CompareICStub {2}
1710    0.0%    0.7%  CallMegamorphic: args_count: 2
1633    0.0%    0.7%  LazyCompile: *Decode native uri.js:208
1509    0.0%    0.6%  LazyCompile: *Readable.read _stream_readable.js:252
1491    0.0%    0.6%  CallMegamorphic: args_count: 1
1457    0.0%    0.6%  LazyCompile: *_nextDomainTick node.js:493
1444    0.0%    0.6%  Stub: ToBooleanStub_UndefinedSpecObject
1411    0.0%    0.6%  Stub: InstanceofStub
1410    0.0%    0.6%  LazyCompile: ~OutgoingMessage.end http.js:915
1339    0.0%    0.5%  LazyCompile: _tickDomainCallback node.js:426
。。。。。。。。。

C++

ticks  total  nonlib   name

GC

ticks  total  nonlib   name
29804    0.2%

Bottom up (heavy) profile

Note: percentage shows a share of a particular caller in the total
amount of its parent calls.
Callers occupying less than 2.0% are not shown.

ticks parent name

11453687   94.9%  /lib64/libc-2.12.so

327343 2.7% /usr/bin/node 18923 5.8% LazyCompile: EventEmitter.addListener events.js:126 14701 77.7% LazyCompile: *Readable.on _stream_readable.js:688 4420 30.1% LazyCompile: *connectionListener http.js:1903 4121 93.2% LazyCompile: EventEmitter.emit events.js:53 4121 100.0% LazyCompile: *onconnection net.js:1163 200 4.5% LazyCompile: *onconnection net.js:1163 89 2.0% LazyCompile: ~EventEmitter.emit events.js:53 89 100.0% LazyCompile: *onconnection net.js:1163 3964 27.0% LazyCompile: *ServerResponse.assignSocket http.js:1108 3964 100.0% LazyCompile: ~parser.onIncoming http.js:2038 3812 96.2% LazyCompile: *parserOnHeadersComplete http.js:69 136 3.4% LazyCompile: ~parserOnHeadersComplete http.js:69 3287 22.4% LazyCompile: *EventEmitter.once events.js:169 1673 50.9% LazyCompile: *Duplex _stream_duplex.js:39 1656 99.0% LazyCompile: *Socket net.js:135 688 20.9% LazyCompile: ~onread net.js:496 542 16.5% LazyCompile: *onSocketEnd net.js:243 495 91.3% LazyCompile: EventEmitter.emit events.js:53 29 5.4% LazyCompile: *onread net.js:496 17 3.1% LazyCompile: ~EventEmitter.emit events.js:53 241 7.3% LazyCompile: *afterShutdown net.js:222 131 4.0% LazyCompile: *onread net.js:496 1220 8.3% LazyCompile: *Socket net.js:135 1217 99.8% LazyCompile: *onconnection net.js:1163

高并发Nodejs参数调整

关闭v8 空时通知机制

--nouse-idle-notification

修改http.Agent

官网说明:
agent.maxSockets
By default set to 5. Determines how many concurrent sockets the agent can have open per host.
(为了http请求能复用connection连接,Nodejs在http.Agent创建了一个默认大小为5的连接池)
修改后如下:
require("http").globalAgent.maxSockets = Infinity;

修改–max-old-space-size

--max-old-space-size=2048(根据自己情况,可以调大,单位是M)
说明:v8 在64位操作系统默认使用的max-old-space-size是1.7G,大家可以通过:node --v8-options 查看V8参数

使用PM2管理

例如:
{
"apps" : [
    {
        "name": "comet-server-4000",
        "script": "server.js",
        "port": 4000,
        "args": "['-p4000','-t','plan']",
        "run-as-group" : "comet",
        "exec_mode": "cluster_mode",
        "node-args": "--nouse-idle-notification --gc_global --max-old-space-size=2048 --max-new-space-size=1024"
    },
    {
        "name": "comet-server-4001",
        "script": "server.js",
        "port": 4001,
        "run-as-group": "comet",
        "args": "['-p4001','-t','plan']",
        "exec_mode": "cluster_mode",
        "node-args": "--nouse-idle-notification --gc_global --max-old-space-size=2048 --max-new-space-size=10240"
    }
]
}

避免在socket.io实时推送项目中使用同步代码,推送项目应该是以中间件的身份出现的,只传输数据

高并发系统参数调整

以Linux为例子 调整文件句柄数

  1. 查看liunx 最大文件句柄数 cat /proc/sys/fs/file-max
  2. 查看进程使用的文件句柄数 ls /proc/pid/fd | wc -l
  3. 查看进程句柄数限制 cat /proc/pid/limits | grep “files”
  4. 修改/etc/sysctl.conf 添加 fs.file-max=1000000

…时间原因待续


35 回复

Nodejs socket.io 高并发实战

socket.io实战

在本案例中,我们通过 nodejs mult process + nginx + redis + socket.io 实现了处理高达50K并发连接的能力,并且正在优化应用以达到100K并发的目标。目前,在50K并发下非常稳定,但在100K并发下会遇到一些问题,如400错误、502错误以及个别进程CPU占用率高达100%。

通过V8自带的性能监控工具分析日志,我们可以看到以下信息:

ticks  total  nonlib   name
11453687   94.9%    0.0%  /lib64/libc-2.12.so
327343    2.7%    0.0%  /usr/bin/node
32393    0.3%    0.0%  /lib64/libpthread-2.12.so

JavaScript性能分析

以下是JavaScript部分的性能分析结果:

ticks  total  nonlib   name
34551    0.3%   13.9%  LazyCompile: *exports._unrefActive timers.js:425
 9061    0.1%    3.6%  Stub: CompareStub_EQ
6462    0.1%    2.6%  LazyCompile: EventEmitter.emit events.js:53
4519    0.0%    1.8%  LazyCompile: EventEmitter.addListener events.js:126
3929    0.0%    1.6%  KeyedLoadIC: A keyed load IC from the snapshot

高并发Nodejs参数调整

为了提高Node.js应用的并发处理能力,我们可以进行以下配置调整:

关闭V8空时通知机制

--nouse-idle-notification

修改http.Agent

// 修改后如下:
require("http").globalAgent.maxSockets = Infinity;

修改最大旧空间大小

--max-old-space-size=2048 (根据实际情况调整,单位是M)

使用PM2管理

{
  "apps": [
    {
      "name": "comet-server-4000",
      "script": "server.js",
      "port": 4000,
      "args": "['-p4000','-t','plan']",
      "run-as-group": "comet",
      "exec_mode": "cluster_mode",
      "node-args": "--nouse-idle-notification --gc_global --max-old-space-size=2048 --max-new-space-size=1024"
    },
    {
      "name": "comet-server-4001",
      "script": "server.js",
      "port": 4001,
      "run-as-group": "comet",
      "args": "['-p4001','-t','plan']",
      "exec_mode": "cluster_mode",
      "node-args": "--nouse-idle-notification --gc_global --max-old-space-size=2048 --max-new-space-size=10240"
    }
  ]
}

避免在socket.io实时推送项目中使用同步代码

推送项目应该以中间件的身份出现,只传输数据。

高并发系统参数调整

以Linux为例 调整文件句柄数

  1. 查看Linux最大文件句柄数

    cat /proc/sys/fs/file-max
    
  2. 查看进程使用的文件句柄数

    ls /proc/pid/fd | wc -l
    
  3. 查看进程句柄数限制

    cat /proc/pid/limits | grep “files”
    
  4. 修改/etc/sysctl.conf 添加

    fs.file-max=1000000
    

以上步骤可以帮助我们更好地优化Node.js应用在高并发场景下的性能表现。


c100K 下报错: untitled1.png

欢迎有兴趣的朋友一起讨论,实际开发中会有很多细节需要注意,这里就不一一列出了

格式化一下代码好吗?加了个精华

看不懂

好奇用什么测试的?另项目地址在那里呀?

这也不是c50k 只是c30k

我也在做这块,关于socket.io这块的性能测试,楼主你是如何去评测?测试client是自己实现还是有现成的工具?用pm2来作为生产环境,socket.io的连接时有session绑定?

show me the code ?

28279是c100k吗? 看来是数学老师死得早

我对这个非常感兴趣, 能否将测试代码共享出来,最近node出0.12.0版本,已经不是0.11的测试版可比的,所以想用pm2@0.12.5 + nodejs@0.12.0 + redis能够做到真正的负载和cluster模式

如果用Cluster 第一个人发的消息 第一次发到a服务器上, 第二次发到B服务器上 这样逻辑会不会出错?

[@yakczh](/user/yakczh) 知道集群是什么意思么?

楼主搜商蛮高的

测试工具是用的什么, 对php的wokerman也测试一起,来个同样环境的pk

我用pm2 cluster 启动项目 websocket 连接出错 你用这种方式没有问题吗? 最后用sticky-session 解决的 生产环境用了forever.

想知道压测逻辑和case

连接断开了,内存适当不了,这个什么原因呢,node0.12 socket.io 1.x

期待楼主的高并发思路与相关讨论和分享的贴

[@luoyjx](/user/luoyjx) 十分感谢 我知道能自己建立
"port": 4001, “run-as-group”: “comet”, “args”: “[’-p4001’,’-t’,’plan’]”, 的作用能告诉我吗 找到解决方法 https://github.com/Unitech/PM2/blob/master/ADVANCED_README.md#json-app-declaration

#弱弱的问一句c50k是啥意思?

楼主您好,最近接受一个NODEJS聊天室项目,并发测试只用不到2k,很是郁闷啊,想用cluster写但是发现文档很少,现在用cluster改写,会遇到进不去聊天室以及进去以后在线用户列表不一致的问题,不知道是什么原因导致的。

Good essay. Marked.

[@dragon2268](/user/dragon2268) 你的2k是怎么来的???

好文章,支持! 自豪地采用 CNodeJS ionic

请问使用的是socket.io哪个版本呢?做app应用还是web应用呢?如果针对app应用,对于android和iOS对应的库貌似只支持0.9X,如何做负载和优化呢?

好文章啊。。谢谢

pm2 和 socket.io 使用有问题啊,好像不能一块使用。

QQ图片20170413183207.png websocket-bench 这个压测靠谱吗

[@hackeridear](/user/hackeridear) 楼主,这些node-args在node v7.x里还有设置的必要吗?

Nodejs Socket.io 高并发实战

介绍

在本帖中,我们将探讨如何通过Node.js、socket.io以及其他工具来实现实时通信应用中的高并发场景。我们主要关注如何通过优化Node.js和socket.io的配置,以及利用多进程和负载均衡技术来达到目标。

示例代码

首先,我们需要安装必要的依赖项:

npm install express socket.io redis

然后,我们可以创建一个简单的服务器:

// server.js
const http = require('http');
const socketIo = require('socket.io');
const redis = require('redis');

const app = require('express')();
const server = http.createServer(app);
const io = socketIo(server);

const redisClient = redis.createClient();

io.adapter(redisAdapter({ host: 'localhost', port: 6379 }));

io.on('connection', (socket) => {
  console.log('A user connected');

  socket.on('disconnect', () => {
    console.log('User disconnected');
  });

  socket.on('message', (msg) => {
    // Handle message and broadcast if necessary
    io.emit('message', msg);
  });
});

server.listen(3000, () => {
  console.log('Server is running on port 3000');
});

在这个例子中,我们使用了socket.io-redis适配器来支持跨多个实例的消息传递,确保消息能在不同的服务器实例之间正确地广播。

优化与调整

  1. 关闭V8空闲通知

    node --nouse-idle-notification server.js
    
  2. 修改HTTP Agent最大连接数

    const http = require('http');
    http.globalAgent.maxSockets = Infinity;
    
  3. 增加Node.js内存限制

    node --max-old-space-size=2048 server.js
    
  4. 使用PM2管理Node.js进程

    {
      "apps": [
        {
          "name": "socket-server",
          "script": "server.js",
          "instances": "max",
          "exec_mode": "cluster",
          "node_args": "--nouse-idle-notification --max-old-space-size=2048"
        }
      ]
    }
    
  5. 调整Linux系统文件句柄限制

    • 修改/etc/sysctl.conf
      fs.file-max = 1000000
      
    • 重启系统或执行sysctl -p使更改生效。
  6. 避免在socket.io中使用同步代码

    • 确保所有业务逻辑都是异步的,并且尽可能减少阻塞操作。

通过上述措施,我们可以在一定程度上提高socket.io应用的并发处理能力。实际部署时还需要根据具体的应用场景和需求进行更详细的性能调优和压力测试。

回到顶部