假设你要使用HTTP请求,你有几个选项,设置一个超时,每次少:
function doTaskWithEnd($uri, $end, $ctx = null) {
if (!$ctx) { $ctx = stream_context_create(); }
stream_context_set_option($ctx, "http", "timeout", $end - time());
$ret = file_get_contents($uri, false, $ctx));
if ($ret === false) {
throw new \Exception("Request failed or timed out!");
}
return $ret;
}
$end = time() + 100;
$fetched = doTaskWithEnd("http://example.com/some/module/fetch", $end);
$ctx = stream_context_create(["http" => ["method" => "POST", "content" => $fetched]]);
$parsed = doTaskWithEnd("http://example.com/some/module/parsed", $end, $ctx);
$ctx = stream_context_create(["http" => ["method" => "PUT", "content" => $parsed]]);
doTaskWithEnd("http://example.com/some/module/save", $end, $ctx);
或者,使用非阻塞解决方案(让我们使用 amphp/amp + amphp/artax):
function doTaskWithTimeout($requestPromise, $timeout) {
$ret = yield \Amp\first($requestPromise, $timeout);
if ($ret === null) {
throw new \Exception("Timed out!");
}
return $ret;
}
\Amp\execute(function() {
$end = new \Amp\Pause(100000); /* timeout in ms */
$client = new \Amp\Artax\Client;
$fetched = yield from doTaskWithTimeout($client->request("http://example.com/some/module/fetch"));
$req = (new \Amp\Artax\Request)
->setUri("http://example.com/some/module/parsed")
->setMethod("POST")
->setBody($fetched)
;
$parsed = yield from doTaskWithTimeout($client->request($req), $end);
$req = (new \Amp\Artax\Request)
->setUri("http://example.com/some/module/save")
->setMethod("PUT")
->setBody($parsed)
;
yield from doTaskWithTimeout($client->request($req), $end);
});
现在,我问,您真的要卸载到单独的请求吗?我们不能假设现在有函数fetch()、parse($fetched) 和save($parsed)?
在这种情况下,这很简单,我们只需设置警报:
declare(ticks=10); // this declare() line must happen before the first include/require
pcntl_signal(\SIGALRM, function() {
throw new \Exception("Timed out!");
});
pcntl_alarm(100);
$fetched = fetch();
$parsed = parse($fetched);
save($parsed);
pcntl_alarm(0); // we're done, reset the alarm
或者,非阻塞解决方案也可以工作(假设 fetch()、parse($fetched) 和 save($parsed) 正确返回 Promises 并且设计为非阻塞):
\Amp\execute(function() {
$end = new \Amp\Pause(100000); /* timeout in ms */
$fetched = yield from doTaskWithTimeout(fetch(), $end);
$parsed = yield from doTaskWithTimeout(parse($fetched), $end);
yield from doTaskWithTimeout(save($parsed), $end);
});
如果您只是希望为不同的顺序任务设置全局超时,我最好在一个脚本中使用pcntl_alarm() 完成所有操作,或者使用流上下文超时选项。
非阻塞解决方案主要适用于您碰巧需要同时做其他事情的情况。例如。如果你想多次执行 fetch+parse+save 循环,彼此独立。