如题,业务需要同一时间请求多个第三方,abc
1> 如果a先返回,判断结果,正确的话,就将结果返回下游。业务结束。
2> 如果a返回错误,就看第二快返回的结果,如果c第二快返回,结果正确,就把c结果返回下游。业务结束。
之前使用 curl_multi 由于这函数本身的bug,一直循环,导致 超时。
理论上说,有几个第三方,就发送几次请求,而这个函数,会重复多次。
只能选天然支持并发的语言。比如go。
但依然不甘心,这些好用的框架,为啥不出个这种功能,类似go的协程呢??
期待大佬解决。
2574
2
0
foreach ($urls as $i => $url) {
$conn[$i] = curl_init($url);
curl_setopt($conn[$i], CURLOPT_RETURNTRANSFER, 1);
curl_multi_add_handle($mh, $conn[$i]);
$result = '';
$status = curl_multi_exec($mh, $active);
curl_multi_select($mh);
$info = curl_multi_info_read($mh);
if (false !== $info) {
$result = curl_multi_getcontent($info['handle']);
// 这里获得某个curl的结果,如果结果ok就break,不ok就等下一个结果
//$is_ok = your_check($result);
$is_ok = 1;
if ($is_ok) {
break;
} while ($status === CURLM_CALL_MULTI_PERFORM || $active);
foreach ($urls as $i => $url) {
curl_close($conn[$i]);
// 最快的正确结果
echo $result;
foreach ($urls as $i => $url) {
$conn[$i] = curl_init($url);
curl_setopt($conn[$i], CURLOPT_RETURNTRANSFER, 1);
curl_multi_add_handle($mh, $conn[$i]);
$result = '';
$status = curl_multi_exec($mh, $active);
curl_multi_select($mh);
$info = curl_multi_info_read($mh);
if (false !== $info) {
$result = curl_multi_getcontent($info['handle']);
// 这里获得某个curl的结果,如果结果ok就break,不ok就等下一个结果
//$is_ok = your_check($result);
$is_ok = 1;
if ($is_ok) {
break;
} while ($status === CURLM_CALL_MULTI_PERFORM || $active);
foreach ($urls as $i => $url) {
curl_close($conn[$i]);
// 最快的正确结果
echo $result;
加上 curl_multi_select($mh); 按道理不会消耗太多cpu才对。
https://www.workerman.net/page/update
webman 1.2.5 有 event-loop设置,config/server.php 里设置成设置'event_loop' => Workerman\Events\Swoole::class 则是用swoole作为底层,可以使用swoole的协程。我对swoole不熟悉,不清楚怎么和webman配合做的你的需求。
你这个接口可能要用worekrman来做了。
嗯,其实需求就是 同一时间请求多个上游,看谁先返回,判断返回结果。
由于整个过程需要300毫秒之内完成。所以 需要根据上游的数量来 同时发出 curl 动作。然后对比结果。
如果for循环的话,需要等待全部上游都返回结果值。这样就超时了。
我对swool协程也不是很清楚。不确定能不能实现这个需求??
curl_multi 原本是最适合的。但可能是本身存在的bug,导致 cpu 一直100% 。
中间用usleep(1)来释放CPU, curl_multi这个不是BUG,官方有说明,是本身执行逻辑就如此
https://bugs.php.net/bug.php?id=61240
https://bugs.php.net/bug.php?id=61141
如果我对swool和协程也不熟悉,不确定能不能实现。
反正现在的curl_multi 坑很多。
1是使用usleep, 本来就要求实效性很紧张的,有些需要200毫秒之内返回,还要减掉处理其他逻辑的时间,所以留给curl的时间就不多了。
2 do { 循环内,会有几千次的无效循环,才会为true,才会进行下一步},这就造成无谓的浪费了。
3 按理说,几个上游url,就请求几次。
4 这属于函数本身的bug了。
use Workerman\Connection\TcpConnection;
use Workerman\Protocols\Http\Request;
use Workerman\Psr7\Response;
class Api
* @var \Workerman\Http\Client
protected $http;
public function __construct()
$this->http = new \Workerman\Http\Client();
public function onMessage(TcpConnection $connection, Request $request)
$connection->sended = false;
$urls = array(
"http://lxr.php.net/",
"http://www.php.net/",
foreach ($urls as $url) {
$this->http->get($url, function(Response $response) use ($connection) {
$body = (string)$response->getBody();
$is_ok = 1; // 根据body判断是否ok
if (!$is_ok) return; // 不ok就return
if (!$connection->sended) { // 已经发送过结果了,不用再发了
$connection->sended = true;
$connection->send($body); // 给客户端发送结果
配置 config/process.php
return [
// 这里省略了其它配置...
'my_api' => [
'handler' => \process\Api::class,
'listen' => 'http://0.0.0.0:1234',
'count' => cpu_count(),
重启webman
在nginx里加一个转发配置
nginx加一个配置,将原有api请求路径的转发到 1234 端口
location /your/api/path {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:1234;
http-client没有设置post数据大小的地方。post数据大小你可以自己计算,用 strlen(http_build_query($post))。
$options = [
'connect_timeout' =>0.26, // 单位秒,0.26就是260毫秒
'timeout' => 0.26,
$this->http = new \Workerman\Http\Client($options );
设置超时这么设置