首页 存档 技术 查看内容

淘宝前端团队:浅谈Node.js和PHP进程管理

2018-3-30 13:00 |来自: 互联网 343 0

摘要: 众所周知,PHP 占据了服务端编程语言的半壁江山,正如汪峰在音乐圈的地位一般。随着 Node.js 逐渐走上服务端编程的舞台,关于 PHP 和 Node.js 孰优孰劣的争论也不曾间断。 垄断性的市场份额足以佐证 PHP 的优秀。并 ...

众所周知,PHP 占据了服务端编程语言的半壁江山,正如汪峰在音乐圈的地位一般。随着 Node.js 逐渐走上服务端编程的舞台,关于 PHP 和 Node.js 孰优孰劣的争论也不曾间断。

垄断性的市场份额足以佐证 PHP 的优秀。并且 HHVM 虚拟机、PHP 7 的革新,也给 PHP 带来了跨越式的性能突破。然而,当我们为语言层面的性能差异喋喋不休时,却往往忽略了 Web 模型在性能表现中的权重。

从 CGI 到 FastCGI

早期的 Web 服务,是基于传统的 CGI 协议实现的。每个发送到服务器的请求,都需要经过启动进程、处理请求、结束进程三个步骤,以至于访问量增大时,系统资源(如内存、CPU 等)开销也巨大,导致服务器性能下降甚至服务中断。

图 1:简单的 CGI 流程示意

在 CGI 协议下,解析器的反复加载是性能低下的主要原因。如果让解析器进程长驻内存,那么它只需启动一次,就可以一直执行着,不必每次都重新 fork 进程,这就有了后来的 FastCGI 协议。

如果 FastCGI 仅仅做到这样,那么和 Node.js 单进程单线程的模型是基本一致的:Node.js 进程启动后保持持续运行,所有的请求都由这个进程接收和处理,当某个请求引起未知错误时,才可能致使进程退出。

事实上 FastCGI 并没有那么简单,为了保证服务的稳定性,他被设计成了多进程调度的模式:

图 2:Nginx FastCGI 执行过程

这个过程同样可以描述为三个步骤:

  • 首先,初始化 FastCGI 进程管理器,并启动多个 CGI 解释器子进程;

  • 接着,当请求到达 Web 服务器时,进程管理器选择并连接一个子进程,将环境变量和标准输入发送给它,处理完成后将标准输出和错误信息返还给 Web 服务器;

  • 最终,子进程关闭连接,继续等待下一个请求的到来;

从 child_process 到 cluster

我们回过头来看看 Node.js 的进程管理方式。

原生 Node.js 的单进程单线程模型是一个极易被喷的槽点。这种机制也决定了 Node.js 天生只支持单核 CPU,无法有效地利用多核资源,一旦进程崩溃,还会导致整个 Web 服务的土崩瓦解。

图 3:简单的 Node.js 的请求模型

和 CGI 一样,单一进程始终面临着可靠性低、稳定性差的问题,当真正服务于生产环境时,这样的弱点相当致命。如果代码本身足够健壮,倒可以在一定程度上避免出错,但同时也对测试工作提出了更高要求。现实中我们无法避免代码 100% 不出纰漏,有些东西容易编写测试用例,有些东西却只能依靠人肉目测。

所幸 Node.js 提供了 child_process 模块,通过简单 fork 即可随意创建出子进程。如果为每个 CPU 分别指派一个子进程,多核利用就完美实现了。于此同时,由于 child_process模块本身继承自 EventEmitter 这个基础类,事件驱动使得进程间的通信非常高效。

图 4:简单的 Node.js master-worker 模型(扒的淘杰老湿的图)

为了简化庞杂的父子进程模型实现,Node.js 紧接着又封装了 cluster 模块,不论是负载均衡、资源回收,还是进程守护,它都会像保姆一样帮你默默地搞定一切。具体技术细节可以参考淘杰老湿的《当我们谈论 cluster 时我们在谈论什么(上)》《当我们谈论 cluster 时我们在谈论什么(下)》,里面有所有关于 cluster 方案的推演和实现,这里不再赘述。

在 Node.js 里,要让应用跑在多核集群上,只需寥寥几行代码就万事大吉了:


var cluster = require('cluster');
var os = require('os');

if (cluster.isMaster) {
for (var i = 0, n = os.cpus().length; i

声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系 [邮箱地址] 删除

路过

雷人

握手

鲜花

鸡蛋

相关分类