详解 PHP 异步后台处置
PHP 异步后台处置
PHP 作为后台的接口效劳器已经很常见,在实际利用场景中经常需要异步后台处置。
引荐:《PHP视频教程》
PHP 当然具有它能作为后台效劳器的优势之处,但是,在处置一些客户端并不关怀的结果时,就显出它的弊端了,没有异步施行的机制。
就比方我们想做一些关于某次客户端拜访php的机能记载(包罗开端时间、完毕时间、此次结果状态等)的记载时,客户端当然想的是php的本次处置能够早点返回,拿到结果,而假如安置常规的方案,客户端就得等php做完机能记载之后,才能拿到结果。
相当于你去银行去查你此刻的余额,而柜员跑过去跟其别人闹了一会儿的磕,在来告诉你的结果一样。
所以,许多时候,就需要一种php能施行异步操纵。
PHP 怎样实现异步处置呢?
其中一种方案就是利用php的系统调取,开启新的进程来实现。
php 供给了fsockopen函数,此函数的功效为初始化一个套接字连接到指定主机,默许状况下将以堵塞模式开启套接字连接。
当然你可以通过stream_set_blocking()将它转换到非堵塞模式。这是关键。
所以,思绪就是:开启一个非堵塞的套接字连接到本机,本机收到之后作一些耗时处置。
相似这样的处置代码(文件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); }
这里,usleep(1000) 非常关键,它能包管这个恳求能发出去。
我们在来看处置的代码逻辑(文件album_write_friends_thread_record.php):
<?php /** * Created by PhpStorm. * User: Administrator * Date: 2016-09-23 * Time: 09:26 */ /** * 客户端调取效劳器接口页面 * user: guwen */ sleep(20);// 睡眠20s ?>
实际上,我们效劳器在施行fsockopen 那段程序时,就不会再等20s之后才能返回给客户端,
而是发出这个恳求之后,即返回客户端,烧毁进程,而把剩余的工作交由其他进程渐渐做去,这就实现了php的异步。
PHP 异步施行的4种常用方式
客户端与效劳器端是通过HTTP和谈停止连接通讯,客户端发起恳求,效劳器端接收到恳求后施行处置,并返回处置结果。
有时效劳器需要施行很耗时的操纵,如处置下载、新闻下发、邮件发送等,这个操纵的结果并不需要返回给客户端。
但由于php是同步施行的,所以客户端需要等候效劳处置完才可以停止下一步。
因此,关于耗时的操纵适合异步施行,效劳器接收到恳求后,处置完客户端需要的数据就先返回,剩余耗时的操纵再异步在效劳器后台施行。
PHP异步施行的常用方式常见的有以下几种,可以按照各自优缺陷停止选中:
1. ajax 恳求
客户端页面采纳AJAX技术恳求效劳器
$.get("doRequest.php", { name: "fdipzone"} ); <img src="doRequest.php?name=fdipzone">
长处:最简便,也最快,就是在返回给客户端的HTML代码中,嵌入AJAX调取,或者,嵌入一个img标签,src指向要施行的耗时足本。
缺陷:一样来说Ajax都应当在onLoad今后触发,也就是说,会员点开页面后,就关闭,那就不会触发我们的后台足本了。
而使用img标签的话,这种方式不克不及称为严厉意义上的异步施行。会员阅读器会长时间等候php足本的施行完成,也就是会员阅读器的状态栏不断显示还在load。
当然,还可以使用其他的相似道理的办法,比方script标签等等。
2. popen()函数
该函数翻开一个指向进程的管道,该进程由派生给定的 command 命令施行而发生。
翻开一个指向进程的管道,该进程由派生给定的 command 命令施行而发生。
所以可以通过调取它,但忽略它的输出。使用代码如下:
// popen — 翻开进程文件指针 resource popen ( string $command , string $mode ) pclose(popen('php /home/fdipzone/doRequest.php &', 'r'));
长处:幸免了第一个办法的缺陷,并且施行速度快。
缺陷:这种办法不克不及通过HTTP和谈恳求别的的一个WebService,只能施行当地的足本文件。并且只能单向翻开,没法穿大量参数给被调取足本。并且假如,拜访量很高的时候,会发生大量的进程。假如使用到了外部资源,还要本人思考竞争。
1)只能在本机施行
2)不克不及传递大量参数
3)拜访量高时会创立许多进程
3. curl 扩展
CURL是一个强大的HTTP命令行工具,可以模拟POST/GET等HTTP恳求,然后得到和提取数据,显示在"标准输出"(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 的连续往下施行,表白这一历程是异步的。
以上就是详解 PHP 异步后台处置的具体内容,更多请关注百分百源码网其它相关文章!