PHP vs Node.js: How FastCGI, Clusters, and ReactPHP Transform Web Performance
This article compares PHP and Node.js server‑side architectures, explaining CGI, FastCGI, Node.js child_process and cluster modules, PHP‑FPM limitations, and how ReactPHP brings event‑driven, asynchronous, non‑blocking I/O to PHP for higher web performance.
It is well known that PHP occupies half of the server‑side programming language market, like a music star, and as Node.js gradually steps onto the server‑side stage, the debate over which is better never stops.
PHP’s dominant market share proves its excellence. HHVM and PHP 7 have brought performance breakthroughs, yet when we focus on language‑level performance we often ignore the impact of the web model.
From CGI to FastCGI
Early web services used the traditional CGI protocol. Each request required starting a process, handling the request, and terminating the process, causing huge memory and CPU overhead under load, leading to performance degradation or service interruption.
Figure 1: Simple CGI flow diagram
In CGI the repeated loading of the interpreter is the main cause of low performance. If the interpreter stays resident in memory, it only needs to start once and can keep running, which led to the FastCGI protocol.
If FastCGI only did that, it would be basically the same as Node.js’s single‑process single‑thread model: after the Node.js process starts it stays alive, handling all requests, and only exits on an unexpected error.
In fact FastCGI is not that simple; to guarantee service stability it is designed as a multi‑process scheduling model:
Figure 2: Nginx + FastCGI execution process
The process can be described in three steps:
First, initialize the FastCGI process manager and start multiple CGI interpreter child processes.
Then, when a request arrives, the manager selects a child, sends environment variables and stdin, receives stdout and error, and returns them to the web server.
Finally, the child closes the connection and waits for the next request.
From child_process to cluster
Let’s look at Node.js process management.
Native Node.js’s single‑process single‑thread model is often criticized. It only uses one CPU core, cannot exploit multi‑core resources, and a crash brings down the whole web service.
Figure 3: Simple Node.js request model
Like CGI, a single process suffers reliability and stability issues in production. Robust code can reduce errors but testing cannot guarantee 100 % correctness; some issues require manual inspection.
Fortunately Node.js provides the child_process module, allowing simple fork to create child processes. Assigning one child per CPU enables perfect multi‑core utilization. Because child_process inherits from EventEmitter, event‑driven inter‑process communication is very efficient.
Figure 4: Simple Node.js master‑worker model (source: Tao Jie)
To simplify the parent‑child model, Node.js also wraps the cluster module, handling load balancing, resource recycling, and process guarding automatically. Details can be found in Tao Jie’s articles, which are omitted here.
Running an application on a multi‑core cluster in Node.js only requires a few lines of code:
var cluster = require('cluster');
var os = require('os');
if (cluster.isMaster) {
for (var i = 0, n = os.cpus().length; i < n; i++) {
cluster.fork();
}
} else {
// start application ...
}So how does FastCGI handle this model?
PHP‑FPM’s inherent drawbacks
PHP‑FPM is PHP’s implementation of FastCGI and the most common, high‑performance process manager for PHP across various SAPIs (cgi, fast‑cgi, cli, isapi, apache). It also implements a parent‑child process model similar to Node.js, ensuring reliability and high performance.
PHP‑FPM follows a typical multi‑process synchronous model: each request maps to a process/thread, and I/O is synchronous and blocking. Although it maintains an independent CGI process pool and the system can manage process lifecycles easily, it cannot handle massive request pressure with a single process like Node.js.
Because of hardware constraints, PHP‑FPM needs reasonable settings in php‑fpm.conf:
pm.max_children # maximum number of child processes
pm.start_servers # number of child processes at startup
pm.min_spare_servers # minimum idle processes; auto‑scale when insufficient
pm.max_spare_servers # maximum idle processes; excess are terminated
pm.max_requests = 1000 # request count threshold after which a child is recycledUnlike JS, PHP processes do not have memory leaks; after handling a request the memory is reclaimed but not returned to the OS, causing PHP‑FPM to occupy a lot of memory that cannot be released, leading to performance drops under high load.
Therefore PHP‑FPM must limit the number of requests per child. Many mistakenly think max_requests controls concurrent connections, but under PHP‑FPM the processes are single‑threaded, so requests cannot run concurrently. The parameter actually provides a request counter; exceeding the threshold triggers process recycling, relieving memory pressure.
You may have noticed the key issue: PHP‑FPM’s architecture is excellent but is bottlenecked by its single‑process performance.
Node.js does not have this problem, while PHP‑FPM’s stability depends on hardware, configuration, and the web server (usually Nginx) load‑balancing capabilities.
ReactPHP, event‑driven, asynchronous, non‑blocking I/O
While PHP 7 enthusiasts were excited, Node.js’s impact grew. ReactPHP has matured, completely abandoning the traditional Nginx + PHP‑FPM stack and adopting Node.js‑style event‑driven, non‑blocking I/O, even its subtitle mirrors Node.js’s. Event‑driven, non‑blocking I/O with PHP. Since most readers are familiar with Node.js, we’ll treat ReactPHP as a PHP version of Node.js. Comparing it with the traditional stack (Nginx + PHP‑FPM, with PHP‑FPM limited to one process) yields:
Figure 5: QPS curve for “Hello World”
Figure 6: QPS curve for SQL queries
The charts show that after transplanting event‑driven, asynchronous, non‑blocking I/O to PHP, QPS remains good even without PHP‑FPM, and performance in I/O‑intensive scenarios improves dramatically.
The event and asynchronous callback mechanism cleverly transforms massive concurrency and high throughput congestion into an asynchronous event queue, handling blocking operations (file reads, DB queries) one by one.
Criticizing the single‑process model may be extreme, but the reliability of a single process has much room for optimization at the web server and process manager level, and high‑concurrency capability ultimately depends on language features—specifically event and async support.
These are strengths of Node.js that PHP lacks natively; ReactPHP can only simulate them, so it is not as perfect as imagined.
Conclusion
When comparing languages, we often focus on the language itself and ignore key surrounding factors.
Take PHP: recent years have brought JIT, opcode caches, AST, HHVM, etc. As these optimizations mature, language‑level issues are no longer the web performance bottleneck. If needed, we can offload heavy tasks to C/C++ via Node.js addons or PHP extensions.
People say PHP is “the best language in the world”; if so, it’s time to learn Node.js’s event‑driven and asynchronous callbacks and consider radically revamping PHP‑FPM. After all, whether Node.js or PHP, our expertise lies in high‑performance web.
(Unless otherwise stated, this article is licensed under CC BY‑NC‑ND 4.0.)
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Node Underground
No language is immortal—Node.js isn’t either—but thoughtful reflection is priceless. This underground community for Node.js enthusiasts was started by Taobao’s Front‑End Team (FED) to share our original insights and viewpoints from working with Node.js. Follow us. BTW, we’re hiring.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
