PHP发起异步请求

古城微笑少年丶 2022-06-17 03:36 291阅读 0赞

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

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

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

  1. function rolling_curl($urls, $callback, $custom_options = null) {
  2. // make sure the rolling window isn't greater than the # of urls
  3. $rolling_window = 5;
  4. $rolling_window = (sizeof($urls) < $rolling_window) ? sizeof($urls) : $rolling_window;
  5. $master = curl_multi_init();
  6. $curl_arr = array();
  7. // add additional curl options here
  8. $std_options = array(CURLOPT_RETURNTRANSFER => true,
  9. CURLOPT_FOLLOWLOCATION => true,
  10. CURLOPT_MAXREDIRS => 5);
  11. $options = ($custom_options) ? ($std_options + $custom_options) : $std_options;
  12. // start the first batch of requests
  13. for ($i = 0; $i < $rolling_window; $i++) {
  14. $ch = curl_init();
  15. $options[CURLOPT_URL] = $urls[$i];
  16. curl_setopt_array($ch,$options);
  17. curl_multi_add_handle($master, $ch);
  18. }
  19. do {
  20. while(($execrun = curl_multi_exec($master, $running)) == CURLM_CALL_MULTI_PERFORM);
  21. if($execrun != CURLM_OK)
  22. break;
  23. // a request was just completed -- find out which one
  24. while($done = curl_multi_info_read($master)) {
  25. $info = curl_getinfo($done['handle']);
  26. if ($info['http_code'] == 200) {
  27. $output = curl_multi_getcontent($done['handle']);
  28. // request successful. process output using the callback function.
  29. $callback($output);
  30. // start a new request (it's important to do this before removing the old one)
  31. $ch = curl_init();
  32. $options[CURLOPT_URL] = $urls[$i++]; // increment i
  33. curl_setopt_array($ch,$options);
  34. curl_multi_add_handle($master, $ch);
  35. // remove the curl handle that just completed
  36. curl_multi_remove_handle($master, $done['handle']);
  37. } else {
  38. // request failed. add error handling.
  39. }
  40. }
  41. } while ($running);
  42. curl_multi_close($master);
  43. return true;
  44. }

自己亲测,服务器端接收请求的结果中返回时间戳,可以看到服务器端接收请求时的时间戳是一模一样的。

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

但是,踩坑开始了。

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

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

于是,决定换代码。

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

![Image 1][]

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

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

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

奉上代码:

  1. use GuzzleHttp\Client;
  2. use GuzzleHttp\Promise;
  3. use GuzzleHttp\Exception\RequestException;
  4. use Monolog\Logger;
  5. use Monolog\Handler\StreamHandler;
  6. function rolling_request($data) {
  7. $client = new Client();
  8. $logger = new Logger('name'); $logger_path = 'test.log';
  9. // 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来记录日志,在接口调试中一定要做好。

[Image 1]:

发表评论

表情:
评论列表 (有 0 条评论,291人围观)

还没有评论,来说两句吧...

相关阅读

    相关 PHP发起异步请求

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

    相关 axios发起get请求

    axios用起来非常方便简洁。下边记录一下。 这里我就新建一个项目,axiostest.html里面就是写测试代码的,然后请求aa.txt里面的数据。这里面我就写了几个字母用