关于协程下异步请求的问题

dgkerry

问题描述

我使用workman并开启了协程,限制只有1个进程运行,我有一个client类,并使用单例模式来实现全局只有一个请求类,代码如下,我发现当我用浏览器请求这个接口,要是一个接口请求完后再请求接口的方式,每次请求这个接口都可以返回数据,但要是我并发请求这个接口,就会报错Value of type null is not callable,有没有大佬解释一下呢?

代码

<?php
    ###client类
    namespace diyvendor;
    use React;
    use React\Async;
    use React\Http\Browser;//版本1.1
    use React\Socket\Connector;
    use React\EventLoop\Loop;
    use diyvendor\logger\Log;
    class client{
        private static $instance;
        protected $timeout=30000;//// in milliseconds
        protected $browser;
        protected $headers=array();
        protected $quoteJsonNumbers=true;
        protected $userAgents = array(
            'chrome' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36',
            'chrome39' => 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36',
            'chrome100' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36',
        );//暂时用不上
        private function __construct(){
            $this->browser=self::setBrowser(array(),$this->timeout);
        }
        public static function getInstance(){
            if(isset(self::$instance) && !empty(self::$instance)){
                return self::$instance;
            }else{
                return self::$instance=new self();
            }
        }
        private static function setBrowser($connector_options=array(),$timeout=1000){
            $connector = new Connector(array_merge(array(
                'timeout' => $timeout,
            ), $connector_options), Loop::get());
            return  (new Browser($connector, Loop::get()))->withRejectErrorResponse(false);
        }
        //该方法会返回$http_status_code, $http_status_text, $url, $method, $response_headers, $response_body, $json_response, $headers, $body
        public function fetch($url='',$method='GET',$headers=null,$body=null,$rand=''){
            return React\Async\async(function () use ($url, $method, $headers, $body,$rand) {
                $headers = array_merge($this->headers, $headers ? $headers : array());
                try {
                    $body = $body ?? '';
                    $result = React\Async\await($this->browser->request($method, $url, $headers, $body));
                }catch (\Exception $e) {
                    $message = $e->getMessage();
                    if (strpos($message, 'timed out') !== false) {
                        //timeOut
                        throw new \Exception(implode(' ', array($url, $method, 28, $message)));
                    }else if (strpos($message, 'DNS query') !== false) {
                        throw new \Exception($message);
                    } else {
                        throw new \Exception($message);
                    }
                }
                $raw_response_headers = $result->getHeaders();//响应头部=-原始数据
                $raw_header_keys = array_keys($raw_response_headers);//响应头部--原始数据--key
                $response_headers = array();//响应头部--处理后的数据--key--value
                foreach ($raw_header_keys as $header) {
                    $response_headers[$header] = $result->getHeaderLine($header);
                }
                $http_status_code = $result->getStatusCode();//code
                $http_status_text = $result->getReasonPhrase();
                $response_body = strval($result->getBody());
                $response_body = $this->_on_rest_response($http_status_code, $http_status_text, $url, $method, $response_headers, $response_body, $headers, $body);

                $json_response = null;
                $is_json_encoded_response = $this->_is_json_encoded_object($response_body);
                if ($is_json_encoded_response) {
                    $json_response = $this->_parse_json($response_body);
                }
                //上面就是对下面的参数进行提取
                return [
                    'request_url'=>$url,
                    'request_method'=>$method,
                    'request_body'=>$body,
                    'request_headers'=>$headers,
                    'response_result'=>isset($json_response)?$json_response : $response_body,
                    'response_headers'=>$response_headers,
                    'response_code'=>$http_status_code,
                    'response_text'=>$http_status_text,
                ];
            }) ();
        }
        private function _parse_json($json_string, $as_associative_array = true){
            return json_decode($this->_on_json_response($json_string), $as_associative_array);
        }
        private function _on_json_response($response_body) {
            return (is_string($response_body) && $this->quoteJsonNumbers) ? preg_replace('/":([+.0-9eE-]+)([,}])/', '":"$1"$2', $response_body) : $response_body;
        }
        private function _is_json_encoded_object($input){
            return ('string' === gettype($input)) &&
                (strlen($input) >= 2) &&
                (('{' === $input[0]) || ('[' === $input[0]));
        }
        private function _on_rest_response($code, $reason, $url, $method, $response_headers, $response_body, $request_headers, $request_body){
            return is_string($response_body) ? trim($response_body) : $response_body;
        }
    }
?>

调用:

use diyvendor\client;
#....某个方法
$client=client::getInstance();
$promisesRaw=array();
$promisesRaw[]=$client->fetch('http://www.baidu.com');
$promisesRaw[]=$client->fetch('http://www.baidu.com');
$promisesRaw[]=$client->fetch('http://www.baidu.com');
$promises =React\Async\await(Promise\all($promisesRaw));
return $this->success('获取成功',$promises);
157 1 0
1个回答

kof21411

协程下最好不要再异步请求

  • dgkerry 1天前

    我的接口处理所需时间比较长(处理过程对外请求次数好几次,每次请求快则几百毫秒,慢则超5秒),开启协程,就是可以提高并发请求进来的处理能力,我使用异步请求是想缩短对外请求好几次所带来的时间过程问题

×
🔝