提升Node.js性能之SO_REUSEPORT的探讨

2024-03-27 21:08

本文主要是介绍提升Node.js性能之SO_REUSEPORT的探讨,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言:多个进程不能同时绑定同一个IP和端口,这是早期Linux内核的一个限制,这个限制给服务器带来了很多不便之处,因为服务器的架构通常不是单进程的,尤其在多核的时代,但是3.9的内核带来了新的特征SO_REUSEPORT。不仅使得服务器的代码逻辑变得简单,对服务器的性能也提升了不少。SO_REUSEPORT的意义是支持同用户下的多个进程同时监听一个IP和端口,本文介绍在Node.js中支持SO_REUSEPORT,以提升Node.js的性能。

目前,Node.js的TCP模块还没有支持SO_REUSEPORT。但是服务器通常是多进程架构的,不管是早期Apache的一个进程处理一个请求的模式,还是现代基于epoll+单线程+多核的模式(Nginx、Redis、Node.js),整体上都是多进程的架构,这就会涉及到一个问题,多个进程如何同时处理请求。传统的方法主要有以下几种(具体可以参考服务器处理连接的架构演变)。
1 主进程接收请求,分发给子进程处理,这种模式的好处是不需要处理多进程同时监听同一个IP端口的问题,因为只有主进程监听。

2 通过fork绕过内核的检查机制。我们看一下一个服务器启动的代码。

int socketfd = socket();
bind(socketfd);
listen(socketfd);

其中在bind这一步,内核会检测IP端口的合法性,如果这个IP和端口已经被监听,那么就会报错,所以如果多个进程同时bind同一个IP端口,那么第二个进程就会失败。但是有办法可以绕过这个限制,那就是不执行bind,我们看看具体怎么做。
2.1 主进程执行bind,这时候的底层架构如下。

2.2 主进程fork多个子进程,这时候的架构如下。

我们看到,fork之后的子进程继承了主进程中的文件描述符fd。这时候子进程不需要执行bind也绑定到了和主进程同样的IP和端口。
2.3 子进程执行listen修改socket状态为监听状态。
这时候就成功地完成了多个进程同时绑定一个IP和端口。

3 通过文件描述符传递的方式绕过内核的检查机制。具体做法如下。
3.1 主进程执行bind
3.2 主进程fork多个子进程,但是不继承主进程fd(fork的时候设置O_CLOEXEC)。
3.3 主进程通过文件描述符传递的方式把fd传给子进程,架构和2.2中的一样。
3.4 子进程执行listen。

Node.js中支持1和3这两种方式,具体在Cluster模块实现。但是上面的几种方式虽然解决了多个进程监听同一个IP端口的问题,但是性能上会存在一些问题。第一种模式存在的问题是,只有主进程可以接收请求,子进程被动接受主进程传过来的请求。那么主进程接收请求的能力会成为服务器的瓶颈。第二第三种模式存在的问题是,当连接到来时,多个进程会同时被唤醒,从而竞争去处理这个连接,连接最终被哪个进程处理,这取决于内核的进程调度,剩下的进程会被无效唤醒,这就是著名的惊群现象,解决惊群现象方式目前有两种,一种是在应用层解决,比如nginx会控制同时只有一个进程阻塞在socket上,保证连接到来时,只有一个进程被唤醒。另一种是在内核解决,新版的内核支持设置标记保证每次只有一个进程被唤醒,哪个进程被唤醒取决于内核实现。这往往会带来服务器代码的复杂处理和多个进程处理连接的负载不均衡。比如在Nginx和Node.js中都有相关的处理(参考Node.js的UV_HANDLE_TCP_SINGLE_ACCEPT标记)。

这时候,SO_REUSEPORT出现了,SO_REUSEPORT更彻底地支持多个进程同时绑定同一个IP端口,架构如下。

SO_REUSEPORT使得每个进程拥有独立的socket和连接队列,内核不仅允许多个进程绑定同一个IP端口,同时在分配连接到对应socket的连接队列时可以做到负载均衡,这使得应用层变得简单了很多。应用层不再需要解决负载均衡的问题,更加不用为了绕过内核对绑定地址的检测想尽办法。这意味着Node.js的Cluster模块很多代码可以不要了。他只需要管理进程,不再需要处理绑定IP端口的问题,同时net模块也变得简单。

支持SO_REUSEPORT不仅(理论上)可以提高Node.js作为服务器的性能,同时也简化了代码的逻辑。不过对Node.js来说,Cluster模块无法从SO_REUSEPORT特性获益,因为Cluster模块的share工作模式本质是通过传递文件描述符的方式让多个进程共享socket的。而不是一个进程一个socket。那么如何从SO_REUSEPORT特性获益呢?直接使用child_process模块,fork多个子进程,每个子进程调用listen函数就行(如果Node.js真的支持SO_REUSEPORT的话,后续Cluster只是作为降级/兼容方案,Node.js可以暴露接口判断是否支持SO_REUSEPORT,不过应用层可能又要封装兼容的代码。。。)。

1 具体pr可以参考https://github.com/libuv/libuv/pull/3198。
2 性能测试可以参考Nginx这个文章
3 SO_REUSEPORT的原理可以参考从内核看SO_REUSEPORT的实现(基于5.9.9)。

这篇关于提升Node.js性能之SO_REUSEPORT的探讨的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/853385

相关文章

用js控制视频播放进度基本示例代码

《用js控制视频播放进度基本示例代码》写前端的时候,很多的时候是需要支持要网页视频播放的功能,下面这篇文章主要给大家介绍了关于用js控制视频播放进度的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言html部分:JavaScript部分:注意:总结前言在javascript中控制视频播放

Python如何使用__slots__实现节省内存和性能优化

《Python如何使用__slots__实现节省内存和性能优化》你有想过,一个小小的__slots__能让你的Python类内存消耗直接减半吗,没错,今天咱们要聊的就是这个让人眼前一亮的技巧,感兴趣的... 目录背景:内存吃得满满的类__slots__:你的内存管理小助手举个大概的例子:看看效果如何?1.

Redis中高并发读写性能的深度解析与优化

《Redis中高并发读写性能的深度解析与优化》Redis作为一款高性能的内存数据库,广泛应用于缓存、消息队列、实时统计等场景,本文将深入探讨Redis的读写并发能力,感兴趣的小伙伴可以了解下... 目录引言一、Redis 并发能力概述1.1 Redis 的读写性能1.2 影响 Redis 并发能力的因素二、

nvm如何切换与管理node版本

《nvm如何切换与管理node版本》:本文主要介绍nvm如何切换与管理node版本问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录nvm切换与管理node版本nvm安装nvm常用命令总结nvm切换与管理node版本nvm适用于多项目同时开发,然后项目适配no

Golang中拼接字符串的6种方式性能对比

《Golang中拼接字符串的6种方式性能对比》golang的string类型是不可修改的,对于拼接字符串来说,本质上还是创建一个新的对象将数据放进去,主要有6种拼接方式,下面小编就来为大家详细讲讲吧... 目录拼接方式介绍性能对比测试代码测试结果源码分析golang的string类型是不可修改的,对于拼接字

mysql线上查询之前要性能调优的技巧及示例

《mysql线上查询之前要性能调优的技巧及示例》文章介绍了查询优化的几种方法,包括使用索引、避免不必要的列和行、有效的JOIN策略、子查询和派生表的优化、查询提示和优化器提示等,这些方法可以帮助提高数... 目录避免不必要的列和行使用有效的JOIN策略使用子查询和派生表时要小心使用查询提示和优化器提示其他常

Node.js net模块的使用示例

《Node.jsnet模块的使用示例》本文主要介绍了Node.jsnet模块的使用示例,net模块支持TCP通信,处理TCP连接和数据传输,具有一定的参考价值,感兴趣的可以了解一下... 目录简介引入 net 模块核心概念TCP (传输控制协议)Socket服务器TCP 服务器创建基本服务器服务器配置选项服

mac安装nvm(node.js)多版本管理实践步骤

《mac安装nvm(node.js)多版本管理实践步骤》:本文主要介绍mac安装nvm(node.js)多版本管理的相关资料,NVM是一个用于管理多个Node.js版本的命令行工具,它允许开发者在... 目录NVM功能简介MAC安装实践一、下载nvm二、安装nvm三、安装node.js总结NVM功能简介N

Springboot中分析SQL性能的两种方式详解

《Springboot中分析SQL性能的两种方式详解》文章介绍了SQL性能分析的两种方式:MyBatis-Plus性能分析插件和p6spy框架,MyBatis-Plus插件配置简单,适用于开发和测试环... 目录SQL性能分析的两种方式:功能介绍实现方式:实现步骤:SQL性能分析的两种方式:功能介绍记录

前端原生js实现拖拽排课效果实例

《前端原生js实现拖拽排课效果实例》:本文主要介绍如何实现一个简单的课程表拖拽功能,通过HTML、CSS和JavaScript的配合,我们实现了课程项的拖拽、放置和显示功能,文中通过实例代码介绍的... 目录1. 效果展示2. 效果分析2.1 关键点2.2 实现方法3. 代码实现3.1 html部分3.2