当前位置:首页 > PHP教程 > php高级应用 > 列表

PHP使用Guzzle发起的异步请求示例详解

发布:smiling 来源: PHP粉丝网  添加日期:2023-10-06 16:32:02 浏览: 评论:0 

这篇文章主要为大家介绍了PHP使用Guzzle发起的异步请求示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪。

使用Guzzle发起异步请求

Guzzle是一个PHP的HTTP客户端,它在发起http请求时不仅可以同步发起,还可以异步发起。

  1. $client = new Client(); 
  2. $request = new Request('GET''http://www.baidu.com'); 
  3. $promise = $client->sendAsync($request)->then(function ($response) { 
  4.     echo $response->getBody(); 
  5. }); 
  6. // todo something 
  7. echo 1; 
  8. $promise->wait(); 

PHP发起HTTP请求的几种方式

curl

使用libcurl库,允许你与各种的服务器使用各种类型的协议进行连接和通讯。

stream

通过流的方式获取和发送远程文件,该功能需要ini配置allow_url_fopen=on。关于php的流更多参考PHP流(Stream)的概述与使用详解

在guzzle中可以兼容使用这两种的任意一种或者是用户自定义的http handler

  1. function choose_handler() 
  2.     $handler = null; 
  3.     if (function_exists('curl_multi_exec') && function_exists('curl_exec')) { 
  4.         $handler = Proxy::wrapSync(new CurlMultiHandler(), new CurlHandler()); 
  5.     } elseif (function_exists('curl_exec')) { 
  6.         $handler = new CurlHandler(); 
  7.     } elseif (function_exists('curl_multi_exec')) { 
  8.         $handler = new CurlMultiHandler(); 
  9.     } 
  10.     if (ini_get('allow_url_fopen')) { 
  11.         $handler = $handler 
  12.             ? Proxy::wrapStreaming($handlernew StreamHandler()) 
  13.             : new StreamHandler(); 
  14.     } elseif (!$handler) { 
  15.         throw new \RuntimeException('GuzzleHttp requires cURL, the ' 
  16.             . 'allow_url_fopen ini setting, or a custom HTTP handler.'); 
  17.     } 
  18.     return $handler

可以看出,guzzle会优先使用curl,然后选择使用stream,Proxy::wrapStreaming($handler, new StreamHandler())  是一个流包装器。

  1. public static function wrapStreaming( 
  2.         callable $default
  3.         callable $streaming 
  4.     ) { 
  5.         return function (RequestInterface $requestarray $optionsuse ($default$streaming) { 
  6.             return emptyempty($options['stream']) 
  7.                 ? $default($request$options
  8.                 : $streaming($request$options); 
  9.         }; 
  10.     } 

什么是URI?URI的组成

URI,Uniform Resource Identifier,统一资源标识符。

scheme:[//[user:password@]host[:port]][/]path[?query][#fragment]

PHP使用Guzzle发起的异步请求示例详解

请求的组装

Guzzle发起请求大致分为两个阶段,第一阶段负责将需要请求的uri组装成各种内部定义的类。

Client类:这是一个发起客户端的调用者,后续所有的调用需要基于这个负责的类实现,它负责提供一个 handler ,这是一个客户端发起http请求的句柄,其中Guzzle实现curl和stream调用的无感知就是在这里实现的,同时开发者也可以自定义请求协议。

  1. // 根据系统当前状态,选择一个发起Http请求的协议的方法句柄 
  2. function choose_handler() 
  3.     $handler = null; 
  4.     if (function_exists('curl_multi_exec') && function_exists('curl_exec')) { 
  5.         $handler = Proxy::wrapSync(new CurlMultiHandler(), new CurlHandler()); 
  6.     } elseif (function_exists('curl_exec')) { 
  7.         $handler = new CurlHandler(); 
  8.     } elseif (function_exists('curl_multi_exec')) { 
  9.         $handler = new CurlMultiHandler(); 
  10.     } 
  11.     if (ini_get('allow_url_fopen')) { 
  12.         $handler = $handler 
  13.             ? Proxy::wrapStreaming($handlernew StreamHandler()) 
  14.             : new StreamHandler(); 
  15.     } elseif (!$handler) { 
  16.         throw new \RuntimeException('GuzzleHttp requires cURL, the ' 
  17.             . 'allow_url_fopen ini setting, or a custom HTTP handler.'); 
  18.     } 
  19.     return $handler

Request类:负责定义一个uri

Promise类:这个类负责承载类请求发起前的各种准备工作完成后的结果,还包括两个回调(请求成功回调、请求失败回调),同时请求发起中的队列,延迟等处理也是在这个类里。

其中组装阶段最重要的方法是私有方法 private function transfer(RequestInterface $request, array $options) ,它负责将用户通过各种方法传入的uri和client类的各种属性组合,然后使用这些属性生成一个新的类 Promise 类。

请求的发起

Client的各种属性组装完成后就可以使用得到的Promise类发起http请求了,这里主要是通过一个 wait() 方法。

同步调用与异步调用

在同步方法内部的调用,同步方法是在内部组装好一个Promise之后立刻发起wait()调用。

  1. public function send(RequestInterface $requestarray $options = []) 
  2.     { 
  3.         $options[RequestOptions::SYNCHRONOUS] = true; 
  4.         return $this->sendAsync($request$options)->wait(); 
  5.     } 

wait的实现

wait() 方法的实现逻辑也很简单,递归调用wait()方法,直到result属性不是PromiseInterface实现类或者state不是pending,然后将结果逐层输出。这里说一下这个state的pending状态,这是一个PromiseInterface实现类的初始化状态,表示改实现类还没有完成,需要继续wait。

  1. public function wait($unwrap = true) 
  2.     { 
  3.         $this->waitIfPending(); 
  4.         $inner = $this->result instanceof PromiseInterface 
  5.             ? $this->result->wait($unwrap
  6.             : $this->result; 
  7.         if ($unwrap) { 
  8.             if ($this->result instanceof PromiseInterface 
  9.                 || $this->state === self::FULFILLED 
  10.             ) { 
  11.                 return $inner
  12.             } else { 
  13.                 // It's rejected so "unwrap" and throw an exception. 
  14.                 throw exception_for($inner); 
  15.             } 
  16.         } 
  17.     } 

waitIfPending() : 如果promise类还处于pending状态就执行。主要是执行改实现类的waitFn方法。最外层promise执行完成后执行queue()->run() `` 这个方法内部循环执行队列内方法,直到队列为空。至此,Guzzle就能将组装进来的多个request,和各种方法执行完毕。

  1. private function waitIfPending() 
  2.     { 
  3.         if ($this->state !== self::PENDING) { 
  4.             return
  5.         } elseif ($this->waitFn) { 
  6.             $this->invokeWaitFn(); 
  7.         } elseif ($this->waitList) { 
  8.             $this->invokeWaitList(); 
  9.         } else { 
  10.             // If there's not wait function, then reject the promise. 
  11.             $this->reject('Cannot wait on a promise that has ' 
  12.                 . 'no internal wait function. You must provide a wait ' 
  13.                 . 'function when constructing the promise to be able to ' 
  14.                 . 'wait on a promise.'); 
  15.         } 
  16.         queue()->run(); 
  17.         if ($this->state === self::PENDING) { 
  18.             $this->reject('Invoking the wait callback did not resolve the promise'); 
  19.         } 
  20.     } 
  21.     public function run() 
  22.     { 
  23.         /** @var callable $task */ 
  24.         while ($task = array_shift($this->queue)) { 
  25.             $task(); 
  26.         } 
  27.     } 

waitFn是什么

回到前面提到的transfer() 函数。

$handler = $options['handler'];

// 返回一个promise类,这个类有一个属性是waitFn

return Promise\promise_for($handler($request, $options));

这里我们看 $handler 是什么?它是一个HandleStack类,就是我们在new Client时选择的发起Http请求的协议的方法句柄,实例化的类。<br />之后的调用依次是 HandleStack->__invoke、RedirectMiddleware->__invoke、PrepareBodyMiddleware->__invoke。执行 $fn($request, $options); 方法,经过前面的逐层处理,此时的$fn就是HandleStack内部的Proxy包装的方法,无论使用哪种协议都会在各自的实现里实例化一个拥有waitFn的Promise的实例。

  1. // curl的实现 
  2.                 $promise = new Promise( 
  3.             [$this'execute'], 
  4.             function () use ($id) { 
  5.                 return $this->cancel($id); 
  6.             } 
  7.         ); 

由此可以直到waitFn方法就是各自协议的实现类的请求发起方法。then() 方法会将promise本身再封装一层promise,并将原先的waitFn和then()的回调方法打包进waitFnList属性里。

queue() 是的入队时机

当请求执行完成后依次调用 processMessages()、promise->resolve()、settle()、FulfilledPromise->then(),将请求结果插入队列。

  1. $queue->add(static function () use ($p$value$onFulfilled) { 
  2.             if ($p->getState() === self::PENDING) { 
  3.                 try { 
  4.                     $p->resolve($onFulfilled($value)); 
  5.                 } catch (\Throwable $e) { 
  6.                     $p->reject($e); 
  7.                 } catch (\Exception $e) { 
  8.                     $p->reject($e); 
  9.                 } 
  10.             } 
  11.         });

Tags: Guzzle PHP异步请求

分享到: