Home >Backend Development >PHP Tutorial >Detailed explanation of PHP asynchronous background processing
PHP Asynchronous Background Processing
PHP is very common as a background interface server, and asynchronous background processing is often required in actual application scenarios.
Recommended: "PHP Video Tutorial"
PHP certainly has the advantage of being a backend server, but it is not easy to handle some clients. When you care about the results, its disadvantages are revealed. There is no asynchronous execution mechanism.
For example, when we want to record some performance records of a certain client's access to PHP (including start time, end time, result status, etc.), of course the client wants to record PHP's current performance. The processing can return early and get the results, but if a conventional solution is installed, the client has to wait for PHP to complete the performance recording before getting the results.
It's equivalent to going to the bank to check your current balance, and the teller ran over and argued with other people for a while, and then came back and told you the same result.
So, many times, you need a php that can perform asynchronous operations.
How does PHP implement asynchronous processing?
One of the solutions is to use php system calls to start a new process to achieve this.
php provides the fsockopen function. The function of this function is to initialize a socket connection to the specified host. By default, the socket connection will be opened in blocking mode.
Of course you can convert it to non-blocking mode through stream_set_blocking(). This is the key.
So, the idea is: open a non-blocking socket to connect to the local machine, and the local machine will do some time-consuming processing after receiving it.
A processing code similar to this (file posttest.php):
$fp = fsockopen($php_Path,80); if (!$fp) { LMLog::error("fsockopen:err" ); } else { $out = "GET /album/action/album_write_friends_thread_record.php?key=&u= HTTP/1.1\r\n"; $out .= "Host: ".$php_Path."\r\n"; $out .= "Connection: Close\r\n\r\n"; stream_set_blocking($fp,true); stream_set_timeout($fp,1); fwrite($fp, $out); usleep(1000); fclose($fp); }
Here, usleep(1000) is very critical, it can ensure that this request can be sent out.
Let’s take a look at the processing code logic (file album_write_friends_thread_record.php):
<?php /** * Created by PhpStorm. * User: Administrator * Date: 2016-09-23 * Time: 09:26 */ /** * 客户端调用服务器接口页面 * user: guwen */ sleep(20);// 睡眠20s ?>
In fact, when our server executes the fsockopen program, it will not wait for 20 seconds before returning. To the client,
but after issuing this request, it returns to the client, destroys the process, and leaves the remaining work to other processes to do slowly, which realizes PHP's asynchrony.
Four common methods of PHP asynchronous execution
The client and server communicate through the HTTP protocol , the client initiates a request, the server performs processing after receiving the request, and returns the processing result.
Sometimes the server needs to perform time-consuming operations, such as processing downloads, message delivery, email sending, etc. The results of this operation do not need to be returned to the client.
But because PHP is executed synchronously, the client needs to wait for the service to be processed before proceeding to the next step.
Therefore, time-consuming operations are suitable for asynchronous execution. After the server receives the request, it will return first after processing the data required by the client, and the remaining time-consuming operations will be executed asynchronously in the background of the server.
Commonly used methods for PHP asynchronous execution include the following. You can choose according to their respective advantages and disadvantages:
1. ajax request
The client page uses AJAX technology to request the server
$.get("doRequest.php", { name: "fdipzone"} ); <img src="doRequest.php?name=fdipzone">
Advantages: The simplest and fastest is to embed the AJAX call in the HTML code returned to the client, or , embed an img tag, and src points to the time-consuming script to be executed.
Disadvantages: Generally speaking, Ajax should be triggered after onLoad. That is to say, if the user clicks on the page and then closes it, our background script will not be triggered.
If you use the img tag, this method cannot be called asynchronous execution in the strict sense. The user's browser will wait for a long time for the execution of the php script to be completed, that is, the status bar of the user's browser always shows that it is still loading.
Of course, you can also use other methods with similar principles, such as script tags and so on.
2. popen() function
This function opens a pipe pointing to the process that is derived from Produced by the execution of a certain command.
Open a pipe pointing to the process spawned by forking the given command execution.
So you can call it but ignore its output. The usage code is as follows:
// popen — 打开进程文件指针 resource popen ( string $command , string $mode ) pclose(popen('php /home/fdipzone/doRequest.php &', 'r'));
Advantages: It avoids the shortcomings of the first method and has fast execution speed.
Disadvantages: This method cannot request another WebService through the HTTP protocol and can only execute local script files. And it can only be opened in one direction, and cannot pass a large number of parameters to the called script. And if the number of visits is high, a large number of processes will be generated. If you use external resources, you have to consider the competition yourself.
1) Can only be executed on the local machine
2) Cannot pass a large number of parameters
3) Many processes will be created when the number of visits is high
3. curl extension
CURL is a powerful HTTP command line tool that can simulate HTTP requests such as POST/GET, and then obtain and extract data , displayed on "standard output" (stdout).
设置curl的超时时间 CURLOPT_TIMEOUT 为1 (最小为1),因此客户端需要等待1秒
代码如下:
<?php $ch = curl_init(); $curl_opt = array( CURLOPT_URL, 'http://www.example.com/doRequest.php' CURLOPT_RETURNTRANSFER,1, CURLOPT_TIMEOUT,1 ); curl_setopt_array($ch, $curl_opt); curl_exec($ch); curl_close($ch); ?>
缺点:如你问题中描述的一样,由于使用CURL需要设置CUROPT_TIMEOUT为1(最小为1,郁闷)。也就是说,客户端至少必须等待1秒钟。
4. fscokopen()函数
fsockopen是最好的,缺点是需要自己拼接header部分。
<?php $url = 'http://www.example.com/doRequest.php'; $param = array( 'name'=>'fdipzone', 'gender'=>'male', 'age'=>30 ); doRequest($url, $param); function doRequest($url, $param=array()){ $urlinfo = parse_url($url); $host = $urlinfo['host']; $path = $urlinfo['path']; $query = isset($param)? http_build_query($param) : ''; $port = 80; $errno = 0; $errstr = ''; $timeout = 10; $fp = fsockopen($host, $port, $errno, $errstr, $timeout); $out = "POST ".$path." HTTP/1.1\r\n"; $out .= "host:".$host."\r\n"; $out .= "content-length:".strlen($query)."\r\n"; $out .= "content-type:application/x-www-form-urlencoded\r\n"; $out .= "connection:close\r\n\r\n"; $out .= $query; fputs($fp, $out); fclose($fp); } ?>
注意:当执行过程中,客户端连接断开或连接超时,都会有可能造成执行不完整,因此需要加上
ignore_user_abort(true); // 忽略客户端断开 set_time_limit(0); // 设置执行不超时
fsockopen支持socket编程,可以使用fsockopen实现邮件发送等socket程序等等,使用fcockopen需要自己手动拼接出header部分
可以参考: http://cn.php.net/fsockopen/
使用示例如下:
$fp = fsockopen("www.34ways.com", 80, $errno, $errstr, 30); if (!$fp) { echo "$errstr ($errno)<br />\n"; } else { $out = "GET /index.php / HTTP/1.1\r\n"; $out .= "Host: www.34ways.com\r\n"; $out .= "Connection: Close\r\n\r\n"; fwrite($fp, $out); /*忽略执行结果 while (!feof($fp)) { echo fgets($fp, 128); }*/ fclose($fp); }
所以总结来说,fscokopen()函数应该可以满足您的要求。可以尝试一下。
fscokopen的问题和popen 一样,并发非常多时会产生很多子进程,当达到apache的连接限制数时,就会挂掉,我问题已经说了这种情况。
PHP 本身没有多线程的东西,但可以曲线的办法来造就出同样的效果,比如多进程的方式来达到异步调用,只限于命令模式。还有一种更简单的方式,可用于 Web 程序中,那就是用fsockopen()、fputs() 来请求一个 URL 而无需等待返回,如果你在那个被请求的页面中做些事情就相当于异步了。
关键代码如下:
$fp=fsockopen('localhost',80,&$errno,&$errstr,5); if(!$fp){ echo "$errstr ($errno)<br />\n"; } fputs($fp,"GET another_page.php?flag=1\r\n"); fclose($fp);
上面的代码向页面 another_page.php 发送完请求就不管了,用不着等待请求页面的响应数据,利用这一点就可以在被请求的页面 another_page.php 中异步的做些事情了。
比如,一个很切实的应用,某个 Blog 在每 Post 了一篇新日志后需要给所有它的订阅者发个邮件通知。如果按照通常的方式就是:
日志写完 -> 点提交按钮 -> 日志插入到数据库 -> 发送邮件通知 ->
告知撰写者发布成功
那么作者在点提交按钮到看到成功提示之间可能会等待很常时间,基本是在等邮件发送的过程,比如连接邮件服务异常、或器缓慢或是订阅者太多。而实际上是不管邮件发送成功与否,保证日志保存成功基本可接受的,所以等待邮件发送的过程是很不经济的,这个过程可异步来执行,并且邮件发送的结果不太关心或以日志形式记录备查。
改进后的流程就是:
日志写完 -> 点提交按钮 -> 日志插入到数据库 --->
告知撰写者发布成功
└ 发送邮件通知 -> [记下日志]
用个实际的程序来测试一下,有两个 php,分别是 write.php 和 sendmail.php,在 sendmail.php 用 sleep(seconds) 来模拟程序执行使用时间。
write.php,执行耗时 1 秒
<?php function asyn_sendmail() { $fp=fsockopen('localhost',80,&$errno,&$errstr,5); if(!$fp){ echo "$errstr ($errno)<br />\n"; } sleep(1); fputs($fp,"GET /sendmail.php?param=1\r\n"); #请求的资源 URL 一定要写对 fclose($fp); } echo time().'<br>'; echo 'call asyn_sendmail<br>'; asyn_sendmail(); echo time().'<br>'; ?>
sendmail.php,执行耗时 10 秒
<?php //sendmail(); //sleep 10 seconds sleep(10); fopen('C:\'.time(),'w'); ?>
通过页面访问 write.php,页面输出:
1272472697 call asyn_sendmail 1272472698
并且在 C:\ 生成文件:
1272472708
从上面的结果可知 sendmail.php 花费至少 10 秒,但不会阻塞到 write.php 的继续往下执行,表明这一过程是异步的。
The above is the detailed content of Detailed explanation of PHP asynchronous background processing. For more information, please follow other related articles on the PHP Chinese website!