php choose handler,PHP HTTP客戶端-Guzzle原理解析

本文適合尋找PHP HTTP客戶端庫,或者對於Guzzle的使用和實現原理比較感興趣的同學閱讀,需要具備一定的PHP基礎知識。

一、背景

在PHP后台開發過程中,經常會遇到模塊間需要通過HTTP通信的情形。PHP語言本身只提供了socket操作的接口,並未提供HTTP相關操作的接口。許多現有的實現采用curl擴展充當HTTP Client與HTTP Server通信,但仍需自己封裝curl的接口。有鑒於此,本文介紹一款流行的PHP HTTP Client客戶端---Guzzle(https://github.com/guzzle/guzzle/)的用法,深入分析其底層實現原理。

二、Guzzle用法

例如使用Guzzle訪問http://www.baidu.com的代碼:

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'http://www.baidu.com', [

"timeout" => 3000

]);

echo $response->getStatusCode(), "\n";

echo $response->getBody();

接口封裝是不是十分簡單?只需要關心請求方法,目標url和請求的選項即可快速上手。同時,Guzzle還支持異步請求方式:

use GuzzleHttp\Exception\RequestException;

use Psr\Http\Message\ResponseInterface;

$client = new \GuzzleHttp\Client();

$promise = $client->requestAsync('GET', 'http://www.baidu.com');

$promise->then(

function (ResponseInterface $res) {

echo $res->getStatusCode() . "\n";

echo $res->getBody();

return $res;

},

function (RequestException $e) {

echo $e->getMessage() . "\n";

echo $e->getRequest()->getMethod();

}

)->wait();

基於異步請求,Guzzle還實現了並發請求,關於Guzzle的具體使用方法可以參考其中文文檔http://guzzle-cn.readthedocs.io/zh_CN/latest/index.html。

三、Guzzle實現原理

1.client構造

GuzzleHttp\Client類構造函數聲明為:

public function __construct(array $config = [])

$config配置使得用戶可以根據需要配置一切可以配置的選項,包括allowredirects、auth、connecttimeout、proxy等。除此之外,還可以自定義請求的處理函數handler,方便應用程序擴展,handler接口規范為:

function handler($request, array $options);

處理成功時,接口返回Psr\Http\Message\ResponseInterface;失敗時返回GuzzleHttp\Exception\RequestException異常。

默認情形下,GuzzleHttp\HandlerStack::create會創建請求處理函數

public static function create(callable $handler = null)

{

$stack = new self($handler ?: choose_handler());

$stack->push(Middleware::httpErrors(), 'http_errors');

$stack->push(Middleware::redirect(), 'allow_redirects');

$stack->push(Middleware::cookies(), 'cookies');

$stack->push(Middleware::prepareBody(), 'prepare_body');

return $stack;

}

create函數以堆棧的形式創建了一系列的處理函數,包括http異常、重定向、cookie和prepare_body。處理函數返回的函數閉包為:

return function (callable $handler) {

return function ($request, array $options) use ($handler) {

...

};

};

函數入參為handler,返回一個新的handler,這樣可以將所有的處理函數鏈接在一起,最終生成一個符合handler接口規范的函數.

choose_handler函數選擇stack中的起始handler,選擇策略為:

擴展自帶curl_multi_exec和curl_exec函數則根據$options中的synchronous選項決定,empty(synchronous)為false則使用CurlHandler,否則使用CurlMultiHandler

擴展只有curl_exec函數則使用CurlHandler

擴展只有curl_multi_exec函數則使用CurlMultiHandler

最后,如果php.ini中開啟了allow_url_fopen,則根據$options中的stream選項決定,empty(stream)為false則使用StreamHandler。

2.client調用request方法

request方法實現為:

public function request($method, $uri = '', array $options = [])

{

$options[RequestOptions::SYNCHRONOUS] = true;

return $this->requestAsync($method, $uri, $options)->wait();

}

由此可見,request事實上是采用了requestAsync異步方法+wait來完成的,也就是異步轉同步。

2.1 requestAsync

requestAsync將請求信息包裝成Psr7\Request對象,然后調用

transfer(RequestInterface $request, array $options)

transfer函數最終返回Promise\promise_for($handler($request, $options)); 其中$handler即為構造函數中所設置的stack,stack中存放一系列的請求處理函數。 HandlerStack的處理函數為:

public function __invoke(RequestInterface $request, array $options)

{

$handler = $this->resolve();

return $handler($request, $options);

}

resolve方法解析整個stack,返回一個包裝后的handler,包裝策略為按照出棧順序包裝,也就是

foreach (array_reverse($this->stack) as $fn) {

$prev = $fn[0]($prev);

}

format,png

典型的中間件模型,所有的處理函數串接在一起了。請求經由http_errors、allow_redirects等處理之后到達Curl,執行真正的網絡交互。

對於同步的handler如CurlHandler,在此處會執行curl_exec發起請求,最終返回的是FulfilledPromise對象或RejectedPromise對象,代表請求已經處理完畢。

對於異步的handler比如CurlMultiHandler,在此處並不會執行curl_multi_exec,而是返回一個promise對象,里面注冊了需要等待執行的curl_multi_exec。

Curl Handler處理完成之后,往上回溯,在Allow_redirects和Http_errors部分會進入then方法,最終返回的結果都是promise對象。

2.2 wait

請求發送完畢,進入promise的wait操作,最終會執行promise的$waitFn函數。

對於CurlMultiHandler,$waitFn即執行curl_multi_exec進行網絡交互,然后調用resolve方法將response對象傳遞到then方法的$onFulfilled函數。

對於CurlHandler,直接利用resolve將response對象傳遞到$onFulfilled函數。

這樣,異步的then方法設置的回調就可以接收到response了。 then方法最終返回response,這個對象又可以作為返回值返回,這樣同步的wait就可以通過返回值來獲取response對象了。

四、總結

本文重點介紹了Guzzle同步和異步請求的實現原理,除此之外,Guzzle還提供了並行請求,請求pool等實現,讀者可以在此基礎上繼續深入。

想要獲取最新技術文章?歡迎訂閱微信公眾號----軟件編程之路

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值