精华 socket.io 高并发实战
发布于 5 年前 作者 hackeridear 37457 次浏览 最后一次编辑是 3 年前 来自 分享

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

…时间原因待续

48 回复

c100K 下报错: untitled1.png

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

本人热爱nodejs,由于刚注册cnodejs.org。以后会有更多好东西和大家分享;希望志同道合者一起讨论,学习

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

@hackeridear 赞,用markdown 格式写

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

这也不是c50k 只是c30k

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

show me the code ?

@chuangtim 这个是线上的运行情况,没有用session绑定,用的redis 做不同进程和不同机器间数据共享;测试的话建议用socket.io-client 去自己写 生产环境用的pm2

@yakczh 这是部分截图,实际有C50K

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

@nike527 没有用cluster,用cluster 需要自己处理粘性回话,保证每个socket 连接都能链接到同一个进程。不过有一个开源模块; sticky-session ,你可以到npm 下载

@aiden0z 测试很简单的 目前有java 客户端测试 还有nodejs 其实 只要知道了socket.io 的协议 用什么测试都可以;我用的nodejs

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

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

@yakczh 知道集群是什么意思么?

楼主搜商蛮高的

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

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

想知道压测逻辑和case

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

@sunzhiguang 可以用nginx 的ip_hash 实现。主要因为socket.io polling 一个周期内有多次请求,你需要保证每个请求都被同一个进程处理,所有这里需要回话保持功能。你可以详细看看: http://socket.io/docs/using-multiple-nodes/

@haozxuan 好的 近期比较忙,晚些时候我分享出来

@lik0914 你好,关于内存的问题看看这篇文章 http://www.w3ctech.com/topic/842

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

@hackeridear 楼主 请问 { “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” } ] } “port”: 4001, “run-as-group”: “comet”, “args”: “[’-p4001’,’-t’,‘plan’]”, 的作用能告诉我吗

@SCWR pm2 有个选项可以生成一个这个文件

    generate                                                   generate an ecosystem.json configuration file
    ecosystem                                                 generate an ecosystem.json configuration file

pm2 generate 试试

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

@SCWR

"run-as-group": "comet"

这个应该是操作系统层面的,已某个用户组运行。 找到一篇文章里有提到 http://www.tuicool.com/articles/vAjIZjn

--run-as-user <run_as_user>		  The user or uid to run a managed process as
--run-as-group <run_as_group>		The group or gid to run a managed process as

不过感觉这个应该是以前的特性,新版的貌似没看到这个选项。

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

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

Good essay. Marked.

@dragon2268 你的2k是怎么来的???

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

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

好文章啊。。谢谢

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

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

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

回到顶部