提升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

相关文章

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

JS 实现复制到剪贴板的几种方式小结

《JS实现复制到剪贴板的几种方式小结》本文主要介绍了JS实现复制到剪贴板的几种方式小结,包括ClipboardAPI和document.execCommand这两种方法,具有一定的参考价值,感兴趣的... 目录一、Clipboard API相关属性方法二、document.execCommand优点:缺点:

Tomcat高效部署与性能优化方式

《Tomcat高效部署与性能优化方式》本文介绍了如何高效部署Tomcat并进行性能优化,以确保Web应用的稳定运行和高效响应,高效部署包括环境准备、安装Tomcat、配置Tomcat、部署应用和启动T... 目录Tomcat高效部署与性能优化一、引言二、Tomcat高效部署三、Tomcat性能优化总结Tom

使用DeepSeek API 结合VSCode提升开发效率

《使用DeepSeekAPI结合VSCode提升开发效率》:本文主要介绍DeepSeekAPI与VisualStudioCode(VSCode)结合使用,以提升软件开发效率,具有一定的参考价值... 目录引言准备工作安装必要的 VSCode 扩展配置 DeepSeek API1. 创建 API 请求文件2.

C#使用yield关键字实现提升迭代性能与效率

《C#使用yield关键字实现提升迭代性能与效率》yield关键字在C#中简化了数据迭代的方式,实现了按需生成数据,自动维护迭代状态,本文主要来聊聊如何使用yield关键字实现提升迭代性能与效率,感兴... 目录前言传统迭代和yield迭代方式对比yield延迟加载按需获取数据yield break显式示迭

Java实现任务管理器性能网络监控数据的方法详解

《Java实现任务管理器性能网络监控数据的方法详解》在现代操作系统中,任务管理器是一个非常重要的工具,用于监控和管理计算机的运行状态,包括CPU使用率、内存占用等,对于开发者和系统管理员来说,了解这些... 目录引言一、背景知识二、准备工作1. Maven依赖2. Gradle依赖三、代码实现四、代码详解五