php使用curl实现伪异步不好使啊

路飞 发布于 2016/01/20 17:14
阅读 2K+
收藏 1

在做A事情的时候,如果符合某条件,则需要另外做一件比较耗时的B事情,B的结果不需要返回给用户,所以考虑用异步方式来做B。

之前查资料说php不推荐使用thread,之前的异步操作都是用消息队列通知另一个Java应用来处理,不过这次这个设计的数据不适合在php和java之间来回转,所以只能php自己来解决了。

有人推荐用fscokopen函数,不过看起来好像有安全风险。又有人说可以用curl来做伪异步,这个看起来比较好一点儿,所以尝试下。

public function test1(){
		error_log('start test1');
		$this->asynch();
		error_log('finish test1');
		echo 'test1';
	}
	
	public function test2(){
		error_log('start test2');
		sleep(10);
		error_log('finish test2');
		echo 'test2';
	}
	
	private function asynch(){
		error_log('start asynch');
		$curl = curl_init();		
		$url = 'http://hostname/test2';		
		curl_setopt ( $curl, CURLOPT_URL, $url );
		curl_setopt ( $curl, CURLOPT_RETURNTRANSFER, true );
		curl_setopt ( $curl, CURLOPT_TIMEOUT, 1 );	// timeout设为1秒		
		curl_exec($curl);
		curl_close($curl);
		error_log('finish asynch');
	}



如上代码所示,请求test1的时候,asynch方法会用curl请求test2,同时设置超时时间为1秒,这样1秒之后curl请求就会断开,test1就能接着执行下去了。

然而现实很残酷,test1确实是顺利执行了,但是test2却一直没有任何动静...

去nginx的access日志里瞄一眼,发现了猫腻

192.168.1.117 - - [20/Jan/2016:15:57:16 +0800] "GET /welcome/test2 HTTP/1.1" 499 0 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36" -
192.168.1.117 - - [20/Jan/2016:15:57:24 +0800] "-" 400 0 "-" "-" -



curl访问test2返回了499,搜索了一下,499是nginx自己定义的错误码,表示在请求未处理完毕之前客户端就关闭了连接。WTF...我就是想主动关闭连接来伪装异步啊。

所以,有人知道怎么不让nginx返回499而是接着执行下去啊




加载中
0
leo108
leo108

在test2的代码最开始地方加上

set_time_limit(0);
ignore_user_abort(true);
fastcgi_finish_request();



leo108
leo108
回复 @路飞 : 那肯定是你test2的代码有问题,看看是不是有设置权限认证之类的
路飞
路飞
加上这些代码之后,nginx的access里test2的请求确实由499变成200了,不过test2剩余的代码依然没有执行,并没有test2的日志打印出来
0
OSC首席键客
OSC首席键客
PHP默认就是连接关闭,进程也就没了的!你curl都关闭了,test2就不会继续执行下去的,也会结束的。
OSC首席键客
OSC首席键客
回复 @路飞 : 楼上的函数没用吗?我用都是有用的。
路飞
路飞
所以异步还是老老实实去用消息队列做是吧。。。心好塞。。。
0
mark35
mark35

用 register_shutdown_function() 配合fastcgi_finish_request()可以实现需求

这是我自己使用的一个类 用于dz。目前一点问题是在某些操作(基本是updatecache之类)中无法获得某些全局变量值。 dbh是PDO驱动


<?php

/*
 * 延迟处理PHP代码,包括DB任务,有助于减小客户端延迟
 * 当PHP以fastcgi运行时配合fastcgi_finish_request()效率更佳 
 *
 * waiting: 2011/11/23
 * update:	2012/01/18
 *
 * example:
 *			$scheduler = new shutdownScheduler($dbh);
 *			$scheduler->register('updatecache');
 *			$scheduler->register('uc_friend_add', $invite['uid'], $uid, '');
 *			
 *			1st works in ucs, 2nd works in ucc...
 *			$this->scheduler->register(array($this, 'delete_useravatar'), $uidsarr); 
 *			$this->scheduler->register('uc_user_deleteavatar', $uidsarr); 
 *
 *			$this->scheduler->register(array($_ENV['note'], 'add'), 'deleteuser', "ids=$uids"); 
 *
 */


if (!function_exists("fastcgi_finish_request")) {
	function fastcgi_finish_request() {
		@ob_flush(); flush();
	}
} 

class shutdownScheduler {
	public $dbh;
	private $callbacks; // array to store user callbacks

	public function __construct($dbh = null) {
		if (empty($dbh)) {
			global $db;
			$this->dbh = $db->dbh;
		}
		else {
			$this->dbh = $dbh;
		}
		if (!$this->dbh) {
			die('shutdownScheduler::dbh ERROR!');
		}
		$this->callbacks = array();
		register_shutdown_function(array($this, 'callRegisteredShutdown'));
	}

	public function register() {
		$callback = func_get_args();

		if (empty($callback)) {
			trigger_error('No callback passed to '.__FUNCTION__.' method', E_USER_ERROR);
			return FALSE;
		}
		if (!is_callable($callback[0])) {
			trigger_error('Invalid callback passed to the '.__FUNCTION__.' method'. $callback[0], E_USER_ERROR);
			return FALSE;
		}
		$this->callbacks[] = $callback;
		return TRUE;
	}

	public function callRegisteredShutdown() {
		$dbh = $this->dbh;
		$dbh->inTransaction() && $dbh->rollBack();

		fastcgi_finish_request();

		if (count($this->callbacks)) {
			$innerTrans = FALSE;
			$dbh->beginTransaction() && $innerTrans = TRUE;

			foreach ($this->callbacks as $arguments) {
				if (!$arguments) { 
					continue; 
				}
				$callback = array_shift($arguments);
				call_user_func_array($callback, $arguments);

			}
			$innerTrans && $dbh->commit() && $innerTrans = FALSE;
		}
	}
}


// ?>




0
eechen
eechen
ab.php
<?php
$ret = a(); //做A事情
fastcgi_finish_request(); //PHP-FPM返回数据给浏览器,结束请求
if($ret) b(); //PHP-FPM继续做B事情

因为b()执行时间不确定,所以ab.php是一个I/O密集型应用,建议配置Nginx把ab.php的请求分发到一个专用的PHP-FPM进程池中,工人进程可以多开几个,避免ab.php的b()业务影响到其他正常的业务.
http://my.oschina.net/eechen/blog/541139
nginx.conf:
location = /ab.php {
    include fastcgi_params;
    fastcgi_pass 127.0.0.1:9001;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
php-fpm.conf:
[ab]
listen = 127.0.0.1:9001
pm = dynamic
pm.max_children = 8
pm.start_servers = 4
pm.min_spare_servers = 4
pm.max_spare_servers = 4
路飞
路飞
好文章,评论质量也很高
返回顶部
顶部