本文内容:Guzzle采用promise方式,通过链式调用requestAsync、getAsync、postAsync等函数嵌套异步请求,并且通过Pool控制并发数量的方法。
需求:
- 需要能够发送异步请求,并且控制一个并发数量。
- 在一个请求发送后需要用到这个请求返回的内容,才能发送下一个请求
类似场景:异步获取指定的一个图像URL列表的图片内容,并把每一张图片都上传到另一个图床上。
需要用到的环境:php 7.2以上版本+Guzzle v7,Guzzle v6的async系列函数有问题,有时候会莫名其妙地输出responseBody,好坑啊。
这里就用httpbin.org来做演示。
首先创建guzzle对象:
$guzzle=new \GuzzleHttp\Client();
正确姿势
$promises=[];// 1
$respList=[];
foreach (range(0,49) as $i){
$promises[]=function() use ($guzzle,&$respList,$i){// 1, 2
$promise=$guzzle->getAsync('https://httpbin.org/uuid');
$promise->then(function($resp) use ($guzzle,&$respList,$i){
$uuid=json_decode($resp->getBody())->uuid;
if(empty($uuid)){
return;// 4
}
echo $i.'获取uuid成功:'.$uuid.PHP_EOL;
$promise2=$guzzle->getAsync('https://httpbin.org/anything?uuid='.$uuid);
$promise2->then(function($resp2) use (&$respList,$i){// 2
$respList[]=json_decode($resp2->getBody());
echo $i.'上传uuid成功'.PHP_EOL;
},function($reason){
echo '上传uuid失败 '.$reason.PHP_EOL;
});
return $promise2;// 3
},function($reason){
echo '获取uuid失败 '.$reason.PHP_EOL;
});
return $promise;// 3
};
}
$pool=new \GuzzleHttp\Pool($guzzle,$promises,[
'concurrency'=>5,
'fulfilled'=>function($index){
},
'rejected'=>function($reason,$index){
}
]);
$pool->promise()->wait();
echo count($respList);
要点,对应上面代码中的注释:
- 用一个promises数组来接收,在循环里面添加一个调用之后返回async promise对象的函数。不要直接添加promise对象。
- 对外部变量进行写操作的时候,需要加上
&
引用,类似指针,不然外部变量不会实质更改。 - 新建promise、配置promise->then、返回promise三步走,包括里层的promise也是。
- 这个地方IDE会提示缺少参数,原因是需要返回一个promise,但其实是不一定需要的。
参考:stackoverflow.com/questions/4…
错误姿势
1.没有用一个function来yield async 请求
$promises=[];
$respList=[];
foreach (range(0,49) as $i){
$promise=$guzzle->getAsync('https://httpbin.org/uuid');
$promise->then(function($resp) use ($guzzle,&$respList,$i){
$uuid=json_decode($resp->getBody())->uuid;
if(empty($uuid)){
return;
}
echo $i.'获取uuid成功:'.$uuid.PHP_EOL;
$promise2=$guzzle->getAsync('https://httpbin.org/anything?uuid='.$uuid);
$promise2->then(function($resp2) use (&$respList,$i){
$respList[]=json_decode($resp2->getBody());
echo $i.'上传uuid成功'.PHP_EOL;
},function($reason){
echo '上传uuid失败 '.$reason.PHP_EOL;
});
return $promise2;
},function($reason){
echo '获取uuid失败 '.$reason.PHP_EOL;
});
$promises[]=$promise;
}
参考:github.com/guzzle/guzz…
2.在第一个promise的then里面调用了第二个promise的wait
foreach (range(0,49) as $i){
$promises[]=function() use ($guzzle,&$respList,$i){
$promise=$guzzle->getAsync('https://httpbin.org/uuid');
$promise->then(function($resp) use ($guzzle,&$respList,$i){
$uuid=json_decode($resp->getBody())->uuid;
if(empty($uuid)){
return;
}
echo $i.'获取uuid成功:'.$uuid.PHP_EOL;
$promise2=$guzzle->getAsync('https://httpbin.org/anything?uuid='.$uuid);
$promise2->then(function($resp2) use (&$respList,$i){
$respList[]=json_decode($resp2->getBody());
echo $i.'上传uuid成功'.PHP_EOL;
},function($reason){
echo '上传uuid失败 '.$reason.PHP_EOL;
});
$promise2->wait();// 1
},function($reason){
echo '获取uuid失败 '.$reason.PHP_EOL;
});
return $promise;
};
}
见代码中的注释1处。guzzle的promise在没有调用wait函数的时候是不会真正执行的。而由于PHP的promise并不是真正的多线程,在promise里面调用另一个promise的wait仍然会阻塞。
参考:github.com/guzzle/prom…
3.误用了EachPromise代替Pool
$pool=new \GuzzleHttp\Promise\EachPromise($promises,[
'concurrency'=>5,
'fulfilled'=>function($index){
},
'rejected'=>function($reason,$index){
}
]);
如果这个错误和前面提到的第一个错误同时犯,就会产生看起来concurrency无效的情况。
参考:github.com/guzzle/guzz…
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!