As is known to all, PHP occupies half of the service-side programming language, as Wang Feng in the music circle of the general status. As node. JS is gradually on the stage of server-side programming, the debate about the merits of PHP and node. JS has never been interrupted.
The monopoly market share is enough to support PHP's excellence. and HHVM virtual machine, PHP 7 innovation, but also to PHP has brought a leap-forward performance breakthrough. However, when we chatter about performance differences at the language level, we tend to overlook the weight of the WEB model in performance.
From CGI to FastCGI
The early WEB services were implemented based on the traditional CGI protocol. Each request sent to the server needs to go through the START process, process the request, end the process three steps, so that the increase in traffic, system resources (such as memory, CPU, etc.) is also a huge cost, resulting in server performance degradation or even service interruption.
Figure 1: A simple CGI flow diagram
Under the CGI protocol, the repeated loading of the parser is the main reason for the low performance. If the parser process is long-standing in memory, then it needs to be started once, it can be executed all the time, do not have to re-fork each process, this has a later FastCGI protocol.
If FastCGI is only doing this, then the model that is single-threaded to node. JS is basically consistent: the node. JS process stays running continuously after it is started, all requests are received and processed by the process, and the process exits when a request causes an unknown error.
In fact, FastCGI is not that simple, in order to ensure the stability of the service, he was designed as a multi-process scheduling mode:
Figure 2:nginx + FastCGI Execution Process
This process can also be described as a three-step procedure:
- First, initialize the FastCGI process Manager and start a number of CGI interpreter sub-processes;
- Then, when the request arrives at the Web server, the process manager selects and connects a child process, sends the environment variable and standard input to it, and returns the standard output and error information to the Web server after processing is completed;
- Finally, the child process closes the connection and continues to wait for the next request to arrive;
From child_process to cluster
We look back at the way node. JS processes are managed.
Native node. JS's single-process one-threaded model is a very easy-to-spray slot point. This mechanism also determines that node. JS is inherently only supported by a single-core CPU, unable to effectively utilize multicore resources, and can cause the entire WEB service to fall apart once the process crashes.
Figure 3: Simple node. JS Request Model
As with CGI, a single process always faces a problem of low reliability and poor stability, a weakness that is quite deadly when it comes to serving the production environment. If the code itself is robust enough, it can avoid errors to some extent, but it also puts a higher demand on the testing effort. In reality we can not avoid the code 100%, some things easy to write test cases, some things can only rely on human flesh visual.
Fortunately, node. JS provides a child_process module that allows you to create child processes at will with simple fork. If a sub-process is assigned to each CPU individually, the multicore utilization is perfectly implemented. At the same time, because the Child_process module itself inherits from the base class of Eventemitter, event-driven makes communication between processes very efficient.
Figure 4: Simple node. js Master-worker model (grilled Amoy old wet figure)
To simplify the complexity of the parent-child process model implementation, node. JS then encapsulates the cluster module, whether it's load balancing, resource recovery, or process daemon, which will help you do everything silently, like a nanny. Specific technical details can refer to Amoy old wet "When we talk about cluster when we talk about what (the)" and "When we talk about cluster what we are talking about (next)", there are all about the cluster program of deduction and implementation, here no longer repeat.
In node. js, it takes just a few lines of code to get the app running on a multi-core cluster:
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 app ...}
So what about the FastCGI protocol, and how does it deal with this model?
Natural defects of PHP-FPM
PHP-FPM is the specific implementation of PHP for the FastCGI protocol, and it is the most common and best performing process manager for PHP in a variety of server-side application programming ports (SAPI:CGI, FAST-CGI, CLI, ISAPI, Apache). It also implements a parent-child process management model similar to node. js, ensuring the reliability and performance of WEB services.
PHP-FPM This model is a very typical multi-process synchronization model, which means that a request corresponds to a process thread, and IO is synchronously blocked. So while PHP-FPM maintains a separate CGI process pool and the system can easily manage the life cycle of the process, it is doomed to fail as node. js, a process that can take on enormous request pressure.
Subject to the hardware facilities of the server, PHP-FPM needs to specify a reasonable php-fpm.conf configuration:
Pm.max_children # Child Process Maximum number of Pm.start_servers # Number of child processes started Pm.min_spare_servers # Minimum number of idle processes, automatic replenishment pm.max_spare_servers when idle process is insufficient Maximum number of idle processes, automatic cleanup when idle process exceeds pm.max_requests = 1000 # Number of child process requests threshold, auto-recycle after
Unlike JS, the PHP process itself does not have a memory leak problem, each process completes the request processing will reclaim memory, but will not be released to the operating system, which results in a large amount of memory is php-fpm occupied and can not be released, the volume of demand increased when the performance plummeted.
So php-fpm need to control the threshold for the number of individual subprocess requests. Many people will mistakenly assume that max_requests controls the number of concurrent connections to the process, in fact the process in PHP-FPM mode is a single thread and the request cannot be concurrent. The real meaning of this parameter is to provide the function of the request counter, which is automatically recycled after the threshold number, to relieve the memory pressure.
Perhaps you have found the key to the problem: Despite the PHP-FPM architecture, the performance of a single process is still stuck.
node. JS is inherently free of this problem, and PHP-FPM cannot guarantee that its stability is constrained by the compatibility of hardware facilities and configuration files, as well as the load scheduling capabilities of WEB servers (usually Nginx) for PHP-FPM services.
reactphp, event-driven, asynchronous execution, non-blocking IO
The mania for PHP 7 masks the onslaught of node. js. When people are still intoxicated with how to choose HHVM or PHP 7, reactphp is also thriving, it thoroughly abandoned the Nginx + php-fpm traditional architecture, to imitate and accept the node. JS's event-driven and non-blocking IO model, even subtitle, have a hair The same:
Event-driven, non-blocking I/O with PHP.
Given that we all know more about node. js, the principle of reactphp is gone, and we can think of it as a PHP version of node. js. Take it and traditional architecture (Nginx + php-fpm, fairness, PHP-FPM only open a process) to do the contrast, the result is this:
Figure 5: The QPS curve when the "Hello World" is output
Figure 6: QPS curve when querying SQL
As we can see, when event-driven, asynchronous execution, non-blocking IO is ported to PHP, the QPS curve is still good even without PHP-FPM support, and performance is multiplied exponentially in IO-intensive scenarios.
The event and asynchronous callback mechanism is awesome, it cleverly resolves large-scale concurrency, high-throughput congestion into an asynchronous event queue, and then solves the blocking (such as file reads, database queries, and so on) in one place.
For the single-process model of the spit groove, perhaps some extreme. However, the obvious fact is that the reliability of the single-process model is greatly optimized at the WEB server and the process manager level, while the high concurrency processing power depends on the language characteristics, which is plainly the support of events and Asynchrony.
These two points must be a natural pride for node. js, but not natively supported in PHP, only by simulating a stepping operation to support the event mechanism like node. js, so reactphp is not as perfect as it might seem.
Conclusion
Most of the time, when we compare the merits and demerits of the language, it is easy to confine itself to the language itself, ignoring some key elements of the package.
In the case of PHP, I've heard so much about the instant compiler (JIT), opcode cache, abstract syntax tree (AST), HHVM, and so on for the past two years. When these optimizations gradually complete, the language level of the problem, has long been no longer the Web performance of the short board. If that doesn't work, we can also hand over complex tasks to C and C + +, in the form of node. js addon or PHP extensions, and it's easy to get it done.
All say PHP is "the best language in the world", so it's time to learn about node. JS Event-driven and asynchronous callbacks, and consider how to revolutionize php-fpm. After all, whether it's node. js or PHP, the place we're good at is ultimately the web, the high-performance web.