PHP发起异步请求

来源:互联网 发布:淘宝客服招聘网站 编辑:程序博客网 时间:2024/04/29 18:31

当我们要与第三方接口进行交互的时候,经常会使用到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 0
原创粉丝点击