PHP发起异步请求

当我们要与第三方接口进行交互的时候,经常会使用到curl来调取接口。但是,我们会面临到一个问题,就是一个页面可能需要调取多个接口,这个时候,用curl的效率可能会有点低,因为是同步调取的。

如果能够可以实现异步调取接口,那是最好的。

这时,我发现了curl_multi函数,用这个函数可以实现异步调取接口。代码奉上:

function rolling_curl($urls, $callback, $custom_options = null) {

    // make sure the rolling window isn't greater than the # of urls
    $rolling_window = 5;
    $rolling_window = (sizeof($urls) < $rolling_window) ? sizeof($urls) : $rolling_window;

    $master = curl_multi_init();
    $curl_arr = array();

    // add additional curl options here
    $std_options = array(CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_MAXREDIRS => 5);
    $options = ($custom_options) ? ($std_options + $custom_options) : $std_options;

    // start the first batch of requests
    for ($i = 0; $i < $rolling_window; $i++) {
        $ch = curl_init();
        $options[CURLOPT_URL] = $urls[$i];
        curl_setopt_array($ch,$options);
        curl_multi_add_handle($master, $ch);
    }

    do {
        while(($execrun = curl_multi_exec($master, $running)) == CURLM_CALL_MULTI_PERFORM);
        if($execrun != CURLM_OK)
            break;
        // a request was just completed -- find out which one
        while($done = curl_multi_info_read($master)) {
            $info = curl_getinfo($done['handle']);
            if ($info['http_code'] == 200)  {
                $output = curl_multi_getcontent($done['handle']);

                // request successful.  process output using the callback function.
                $callback($output);

                // start a new request (it's important to do this before removing the old one)
                $ch = curl_init();
                $options[CURLOPT_URL] = $urls[$i++];  // increment i
                curl_setopt_array($ch,$options);
                curl_multi_add_handle($master, $ch);

                // remove the curl handle that just completed
                curl_multi_remove_handle($master, $done['handle']);
            } else {
                // request failed.  add error handling.
            }
        }
    } while ($running);
    
    curl_multi_close($master);
    return true;
}
自己亲测,服务器端接收请求的结果中返回时间戳,可以看到服务器端接收请求时的时间戳是一模一样的。

也就是说请求是同时到达的。


但是,踩坑开始了。

不知道为何,当我一次性发起请求的时候,有时候会出现很奇葩的情况,服务器端接口接收到的参数混乱了,即接口2接收到了接口1的参数。

这个问题找了很久,都没有找到解决办法。

于是,决定换代码。


于是我又发现了guzzleHttp这个东西。guzzle的介绍是这样的。http://guzzle-cn.readthedocs.io/zh_CN/latest/index.html


这正是我需要的。guzzle的使用很简单,不管是使用同步还是异步。

可是使用guzzle之后,上面的问题还是存在。不晓得是服务器端的问题还是php异步自身的问题。

于是想了个办法,当发现请求结果为空的时候,再次发起一次同步请求。(虽然效率不高的说,但是总得获取到数据啊)


奉上代码:

use GuzzleHttp\Client;
use GuzzleHttp\Promise;
use GuzzleHttp\Exception\RequestException;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;

function rolling_request($data) {
    $client = new Client();
    $logger = new Logger('name');
$logger_path = 'test.log';
// Initiate each request but do not block $promises = []; $logger->pushHandler(new StreamHandler($log_path), Logger::INFO); $logger->addInfo('============================BEGIN REQUEST========================='); foreach($data as $key => $item) { $logger->addInfo('url: '.$item['url']); $logger->addInfo('data:'.json_encode($item['params'])); try { $promises[$key] = $client->requestAsync('POST', $item['url'], ['connect_timeout' => 6, 'http_errors' => false, 'form_params' => $item['params']]); $promises[$key]->then( function (ResponseInterface $res) { }, function (RequestException $e) { } ); } catch (RequestException $e) { //ToDO: 异常处理 $logger->addError('RequestException: '.$e->getMessage()); } } // Wait on all of the requests to complete. try { $results = Promise\unwrap($promises); } catch (RequestException $e) { //ToDO: 异常处理 $logger->addError('RequestException: '.$e->getMessage()); } $return = []; if($results) { foreach($results as $key => $value) { $content = $value->getBody()->getContents(); //STANGE:不知道为什么,发起异步请求的时候,服务器端接收到的参数有时候会对不上,为了避免这种情况,多做一个判断 // 当返回结果为空的时候,再发起一个同步请求 if($content && $content['code'] == 'SUCCESS' && empty($content['result'])) { //echo '---又有空的东西啦---<br />'; $logger->addWarning('content:'.$key.': 异步请求失败,将此请求转换成同步请求'); //TODO: 另外写一个同步请求的程序,重新请求 } else { $return[$key] = $content; } } } $logger->addInfo('============================END MULTI REQUEST==========================='); $logger->addInfo(''); //exit; return $return;}

为了测试同步和异步的区别,特意价格服务器端接口地址改为不可用的地址,超时都设置为6秒,一次性发起4个请求。

在同步的情况下,整个页面返回会消耗24秒多。

在异步的情况下,整个页面返回只需要消耗6秒多。


另,上面用到了Monolog来记录日志,在接口调试中一定要做好。



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值