Home >Backend Development >PHP Tutorial >Why does PHP need asynchronous programming? Detailed explanation of PHP asynchronous programming (with examples)
My knowledge about PHP asynchronous is still quite confusing. I am writing this article to sort it out. There may be errors.
Traditional php-fpm When a process executes a request, how many processes must be generated to achieve the same concurrency. What's worse is that each request needs to be recompiled and executed, causing concurrency to never increase. Hence the emergence of Swoole and WorkerMan are two popular resident memory frameworks in China [1]. The principles of these two frameworks are to use event loops to keep the program in memory, waiting for external requests, and achieve high concurrency.
Why asynchronous is needed
Let’s look at an example first
Create a new file slowServer.php in the working directory
<?php sleep(5); // 5秒后才能返回请求 echo 'done';
Start the service
$ php -S localhost:8081 slowServer.php
Open another terminal and install dependencies
$ pecl install event # 安装 event 扩展 $ composer require workerman/workerman $ composer require react/http-client:^0.5.9
Create a new file worker.php
require_once __DIR__ . '/vendor/autoload.php'; use Workerman\Worker; use Workerman\Connection\AsyncTcpConnection; use Amp\Artax\Response; $http_worker = new Worker("http://0.0.0.0:8082"); $http_worker->count = 1; // 只开一个进程 $http_worker->onMessage = function($connection, $host) { echo 1; $data = file_get_contents('http://localhost:8081'); $connection->send($data); }; Worker::runAll();
Open the server
php worker.php start
Open two tabs in the browser, Both open the URL http://localhost:8082. At this time, you can see that the terminal outputs "1", and after a while it outputs "1" again. The reason is that the 8081 server is blocked in waiting for the 8081 return when processing the first request. After the first request is completed, it will Start processing the second request. That is to say, requests are executed one by one. How many processes need to be established to achieve the same number of concurrencies, just like php-fpm. Now modify the code
$http_worker->onMessage = function($connection, $host) { echo 1; $loop = Worker::getEventLoop(); $client = new \React\HttpClient\Client($loop); $request = $client->request('GET', 'http://localhost:8081'); $request->on('error', function(Exception $e) use ($connection) { $connection->send($e); }); $request->on('response', function ($response) use ($connection) { $response->on('data', function ($data) use ($connection) { $connection->send($data); }); }); $request->end(); };
Now open the service and initiate a request in the browser. It is found that the second "1" is output immediately after the request, and the first request has not yet ended. This indicates that the process is no longer blocked and the amount of concurrency depends on the cpu and memory, not the number of processes.
Why asynchronous is needed
It is clear from the above example that the reactphp framework makes the http request asynchronous and makes the onMessage function non-blocking. The CPU can to handle the next request. That is, the cpu loop waits for 8081 to return, turning into epoll waiting.
The meaning of asynchronous is to free the CPU from io waiting and can handle other computing tasks. If you want to know how to use the framework to achieve asynchronous, just see here. WorkerMan, combined with ReactPHP or its own AsyncTcpConnection, can already meet the asynchronous needs of many io requests. Let's continue to discuss how these frameworks are asynchronous.
Which places should be made asynchronous
Through the above example, we already know that once the CPU is not required for execution, but when waiting for io, the io process should be Made asynchronous.
Implementing event loop
The above example uses reactphp to turn the http request into asynchronous. In fact, the WorkerMan framework itself is also asynchronous. Let’s take a look at how WorkerMan works. Enable the onMessage function to accept requests asynchronously. First, create the following file react.php
<?php $context = stream_context_create(); $socket = stream_socket_server('tcp://0.0.0.0:8081', $errno, $errmsg, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN,$context); // 注册一个 fd(file descriptor) function react($socket){ $new_socket = stream_socket_accept($socket, 0, $remote_address); echo 1; } $eventBase = new EventBase(); $event = new Event($eventBase, $socket, Event::READ | Event::PERSIST, 'react', $socket); // 注册一个事件,检测 fd 有没有写入内容 $event->add(); $eventBase->loop(); // 开始循环
Start execution
$ php react.php
Execute in another terminal
telnet 127.0.0.1 8081
At this time, you will see the first terminal output '1 '.
I have written an article before "php uses epoll", which is the basis of this article. In that article, the event callback is implemented through timing, that is,
$event->add($seconds);
. But here, the event callback is implemented by detecting whether fd has written content. This process does not require the participation of the CPU. When fd has content written to it, the function 'react' will be called, and the cpu will start to be used. If the process executes another asynchronous request at this time, such as requesting a web page using the reactphp framework, then the program will release the CPU. If another request comes in at this time, it can call back to execute another 'react' function. This increases the amount of concurrency.
Coroutine
Generater
This is the PHP official document of the generator http:// php.net/manual/zh/lang...<?php function gen_one_to_three() { for ($i = 1; $i <= 3; $i++) { //注意变量$i的值在不同的yield之间是保持传递的。 yield $i; } } $generator = gen_one_to_three(); foreach ($generator as $value) { echo "$value\n"; }The generator saves the state every time the program is executed to yield, and then returns $i. Whether to continue executing the loop in gen_one_to_three depends on the main program Whether to continue calling
What is a coroutine
Another way to write the above program is<?php $i = 1; function gen_one_to_three() { global $i; if ($i<=3){ return $i++; } } while ($value = gen_one_to_three()) { echo "$value\n"; }It can be seen that a coroutine is a kind of The encapsulation of a function turns it into a function that can be interrupted and behaves more like a sub-process or sub-thread than a function. The specific writing method of coroutine will not be detailed here, because the writing method of coroutine is very complicated and may require another layer of encapsulation to be easy to use.
Coroutine and asynchronous
Since the coroutine can be interrupted, then as long as the event loop is initiated after the program initiates the request, and then returns with yield, then the program continues to execute the main In the program part, wait for the event to return, trigger the function, and execute Generatot::next() or Generator::send() to continue executing the coroutine part. After encapsulation, it is as if there is no asynchronous callback function, which is very similar to a synchronous function. There are now two frameworks, ampphp and swoole, that encapsulate coroutines. If you are interested, you can learn about them.The above is the detailed content of Why does PHP need asynchronous programming? Detailed explanation of PHP asynchronous programming (with examples). For more information, please follow other related articles on the PHP Chinese website!