<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # 5.1 協程 在前面的章節[2.3](../chapter-2/2.3-協程原理.md)介紹了協程的原理及PHP用戶空間如何實現協程,本節將重點介紹MSF框架的協程如何使用,同時會剖析PHP工程級協程調度器的實現及調度算法。 ## 為什么要使用協程? 至于為什么使用協程,可能大家看法還不統一,這里舉例來說明,我們實現一個接口,此接口內包含: 2次http請求其他接口A、B, 1次Redis請求C 他們之間的依賴關系為:`((A && B) || C)` ```php <?php /** * 協程示例控制器 * * @author camera360_server@camera360.com * @copyright Chengdu pinguo Technology Co.,Ltd. */ namespace App\Controllers; use PG\MSF\Controllers\Controller; use PG\MSF\Client\Http\Client; class CoroutineTest extends Controller { /** * 異步回調的方式實現(A && B) || C */ public function actionCallBackMode() { $client = new \swoole_redis; $client->connect('127.0.0.1', 6379, function (\swoole_redis $client, $result) { $client->get('apiCacheForABCallBack', function (\swoole_redis $client, $result) { if (!$result) { swoole_async_dns_lookup("www.baidu.com", function($host, $ip) use ($client) { $cli = new \swoole_http_client($ip, 443, true); $cli->setHeaders([ 'Host' => $host, ]); $apiA = ""; $cli->get('/', function ($cli) use ($client, $apiA) { $apiA = $cli->body; swoole_async_dns_lookup("www.qiniu.com", function($host, $ip) use ($client, $apiA) { $cli = new \swoole_http_client($ip, 443, true); $cli->setHeaders([ 'Host' => $host, ]); $apiB = ""; $cli->get('/', function ($cli) use ($client, $apiA, $apiB) { $apiB = $cli->body; if ($apiA && $apiB) { $client->set('apiCacheForABCallBack', $apiA . $apiB, function (\swoole_redis $client, $result) {}); $this->outputJson($apiA . $apiB); } else { $this->outputJson('', 'error'); } }); }); }); }); } else { $this->outputJson($result); } }); }); } /** * 協程的方式實現(A && B) || C */ public function actionCoroutineMode() { // 從Redis獲取get apiCacheForABCoroutine $response = yield $this->getRedisPool('tw')->get('apiCacheForABCoroutine'); if (!$response) { // 從遠程拉取數據 $request = [ 'https://www.baidu.com/', 'https://www.qiniu.com/', ]; /** * @var Client $client */ $client = $this->getObject(Client::class); $results = yield $client->goConcurrent($request); // 寫入redis $this->getRedisPool('tw')->set('apiCacheForABCoroutine', $results[0]['body'] . $results[1]['body'])->break(); $response = $results[0]['body'] . $results[1]['body']; } // 響應結果 $this->outputJson($response); } } ``` 示例代碼: [https://github.com/pinguo/php-msf-demo/app/Controllers/CoroutineTest.php](https://github.com/pinguo/php-msf-demo/blob/master/app/Controllers/CoroutineTest.php) http://127.0.0.1:8000/CoroutineTest/CoroutineMode http://127.0.0.1:8000/CoroutineTest/CallBackMode 1. Swoole實現了異步非阻塞的IO模型它是高性能的基礎,但是書寫邏輯代碼非常復雜,需要多層嵌套回調,閱讀和維護困難 2. 基于Yield的協程可以用同步的代碼編寫方式,達到異步IO的效果和性能,避免了傳統異步回調所帶來多層回調而導致代碼無法維護 ## 協程的調度順序 ```php <?php /** * 協程示例控制器 * * @author camera360_server@camera360.com * @copyright Chengdu pinguo Technology Co.,Ltd. */ namespace App\Controllers; use PG\MSF\Controllers\Controller; use PG\MSF\Client\Http\Client; class CoroutineTest extends Controller { // 略... // 姿勢一 public function actionNested() { $result = []; /** * @var Client $client1 */ $client1 = $this->getObject(Client::class, ['http://www.baidu.com/']); yield $client1->goDnsLookup(); /** * @var Client $client2 */ $client2 = $this->getObject(Client::class, ['http://www.qq.com/']); yield $client2->goDnsLookup(); $result[] = yield $client1->goGet('/'); $result[] = yield $client2->goGet('/'); $this->outputJson([strlen($result[0]['body']), strlen($result[1]['body'])]); } // 姿勢二 public function actionUnNested() { $result = []; /** * @var Client $client1 */ $client1 = $this->getObject(Client::class, ['http://www.baidu.com/']); $dns[] = $client1->goDnsLookup(); /** * @var Client $client2 */ $client2 = $this->getObject(Client::class, ['http://www.qq.com/']); $dns[] = $client2->goDnsLookup(); yield $dns[0]; yield $dns[1]; $req[] = $client1->goGet('/'); $req[] = $client2->goGet('/'); $result[] = yield $req[0]; $result[] = yield $req[1]; $this->outputJson([strlen($result[0]['body']), strlen($result[1]['body'])]); } } ``` ### 解析 兩種姿勢看上去都使用了協程的yield關鍵字;姿勢一由于協程在調度時,第一個yield沒有接收數據時,程序控制流就不會往下繼續執行,從而退化為串行請求第三方接口;姿勢二由于DNS查詢是異步的,就同時進行多個DNS查詢,通過yield關鍵獲取協程執行的結果,再同時異步請求多個接口,最后通過yield關鍵字獲取接口響應結果。 通過兩種姿勢的對比,使用php-msf協程yield關鍵字,很好的解決了異步IO回調的寫法,讓程序看上去是同步執行的,yield起來了接收數據的作用,這也是前面所說的yield具有雙向通信的最要特性。 姿勢一協程調度過程 send http dns1 lookup->rev http dns1 lookup->send http dns2 lookup->rev http dns2 lookup->send get1->rev get1->send get2-> rev get2 姿勢二協程調度過程 send http dns1 lookup->send http dns2 lookup->rev http dns1 lookup->rev http dns2 lookup->send get1->send get2->rev get1->rev get2 **需要特別注意的是: 如果某個異步IO操作不需要獲取返回值,如設置緩存set key val,框架允許不加yield關鍵字,需要調用break()方法,這可以大大的提升接口的并發能力** ## 使用MSF協程 通過上述的示例代碼,我們不難得出MSF協程的使用方式,通常情況下,我們使用異步客戶端發送請求,使用yield關鍵字獲取協程任務的運行結果。 ### Http ```php <?php function Http() { // 獲取Http客戶端 $client = $this->getObject(\PG\MSF\Client\Http\Client::class); // 單個接口GET請求(自動完成DNS查詢->Http請求發送->獲取結果) $res = yield $client->goSingleGet('http://www.baidu.com/'); $client = $this->getObject(\PG\MSF\Client\Http\Client::class); // 單個接口POST請求(自動完成DNS查詢->Http請求發送->獲取結果) $res = yield $client->goSinglePost('http://www.baidu.com/'); // 多個接口同時請求(自動完成DNS查詢->Http請求發送->獲取結果) $client = $this->getObject(\PG\MSF\Client\Http\Client::class); $res = yield $client->goConcurrent(['http://www.baidu.com/', 'http://www.qq.com']); $client = $this->getObject(\PG\MSF\Client\Http\Client::class); // 手工DNS查詢 yield $client->goDnsLookup('http://www.baidu.com/'); // 手工發送HTTP請求 $res = yield $client->goGet('/'); } ``` ### Task ```php <?php function Task() { $idAllloc = $this->getObject(Idallloc::class); $newId = yield $idAllloc->getNextId(); } ``` ### Redis ```php <?php function RedisCoroutine() { $sendRedisGet = $this->getRedisPool('tw')->get('apiCacheForABCoroutine'); $cache = yield $sendRedisGet; } ``` ## MSF協程調度器 MSF協程基于Generator/Yield,IO事件觸發,是自主研發的調度器,它的核心思想是發生任何異步IO操作之后,程序的控制流就切換到其他請求,待異步IO可讀之后,由程序自行調用調度器接口,進行一次調度,并響應請求。MSF協程完全拋棄了定時器每隔一定時間輪詢任務的方案,使得調度器的性能更加接近原生的異步回調方式。 ### 關鍵技術點 * Generator/Yield * SplStack * taskMap ### 主要特性 * 協程獨立堆棧 * 支持嵌套 * 全調用鏈路異常捕獲 * 調度統計 * 多入口調度 ### 協程調度流程 ![協程執行流程圖](../images/協程執行流程圖V2.png "協程執行流程圖")
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看