PHP純異步非阻塞框架:swPromise

jopen 9年前發布 | 32K 次閱讀 swPromise PHP開發

swPromise 是基于 swoole 的 PHP promise 框架。

在日常的使用場景中,PHP一般用作接口聚合層。一個業務請求可能會串行的請求多個接口A->B->C,此時如果接口B的響應時間較慢(關鍵性業務,需要有較長的timeout等待時間),則會導致請求整體的時間過長,嚴重降低系統的響應能力。考慮到這個業務場景下,進程的主要時間用在等待網絡io返回。如果能夠使用異步編程的方式,則會極大的提升服務的吞吐量(NodeJS的優勢)。

如果某接口響應時間超過往常,會導致php-fpm進程數急劇升高,從而導致大量cpu資源浪費在進程調度上面,甚至導致服務崩潰。swPromise框架是為了解決該問題而開發的。

異步非阻塞模式是實現高性能網絡編程的一種方法。傳統上,為進行異步調用,會在代碼中實現大量的回調函數,導致代碼可讀性與可維護性的急劇下降。為了解決這個問題,主流方案有以下幾種:

  • 自定義事件式方案

  • Promise/Deferred

  • 高階函數篡改回調函數

  • 協程(Generator)

Swoole 是PHP語言的高性能網絡通信框架,提供了PHP語言的異步多線程服務器。 swoole采用自定義事件式方案,為我們提供網絡層基本封裝。基于swoole,可以擴展出業務層的異步開發框架。

tsf (Tencent Server Framework) 是騰訊公司推出的 PHP 協程方案,基于 Swoole+PHP Generator 實現的 Coroutine。該框架使用協程模式,基于swoole與swoole framework開發。實現了真正的異步非阻塞開發模式,同時具有極高的性能。其核心代碼來源于該文章Cooperative multitasking using coroutines (in PHP!) 。 tsf使用了較為復雜的用戶態任務調度邏輯,暫時沒有看到生產環境的使用案例。另外由于使用了swoole framework,也使其略顯重量級。

swPromise的主要處理流程在Core\Async\Promise類中。該類實現了基本的then方法,并通過對promise流程的延遲計算,保證了異步流程的動態控制能力。該框架是一個非常基礎的web框架,目前僅實現通用Future(通用延遲計算)、HttpClientFuture、ResponseFuture三個延遲計算類。

該框架需要配合Swoole、php-http-parser擴展使用,第二個擴展用于解析http協議。

演示代碼

class Handler_Index extends \Core\Handler{
    public function run($request, $response){
        Promise::create ( Model::getUserInfo ( 'user1', 'haha' ) )
            ->then (function(&$promise){
                $user1 = $promise->get('user1');
                if($user1){
                    return Model::getUserInfo ( 'user2', 'haha2' )
                            ->then(function(&$promise){
                                $user2 = $promise->get('user2');
                                $promise->accept(['user3'=>$user2['body']]);
                            });
                }
                else $promise->accept();
            })
            ->then ( Model::getUserInfo ( 'user4', 'haha4' ) )
            ->then ( Model::getUserInfo ( 'user5', 'haha5' ) )
            ->then ( new ResponseFuture ($response) )
            ->start ( new PromiseContext () );
    }
}

這段流程表明了,先獲取haha這個用戶的信息,寫入上下文的user1字段中。如果獲取到了數據,再獲取haha2這個用戶的信息,寫入上下文user2字段中。并將user2的body字段放入user3字段中。然后獲取haha4和haha5的信息。最后將所有數據輸出到網頁。

可以看到,在第一個then中,通過if條件返回promise對象,實現了對異步流程的動態控制。同樣的,整個流程通過then串聯起來,已經較為接近同步代碼的書寫了。而使用回調的方式,代碼會變得極為恐怖。

并行請求

class Handler_Index extends \Core\Handler{
    public function run($request, $response){
        Promise::create([
            Model::getUserInfo ( 'user1', 'haha' ),
            Model::getUserInfo ( 'user2', 'haha2' ),
        ])->then(
            new ResponseFuture ($response)
        )->start(new PromiseContext ());
    }
}

這個請求并行獲得haha與hah2兩個用戶的數據,分布放到user1和user2兩個字段中。

存在問題

其中Handler_Sync實現的就是該框架同步的使用方式。另外,目前reject方法以及異常處理流程均沒有實現,有興趣的朋友可以自行擴展。

目前有一個比較嚴重的bug,如果大量http request沒有完成就自行中斷的話,會導致swoole http server發生錯誤,從而退出。在swoole前面放一個nginx就可以解決問題。

測試方法

啟動

php run.php

測試:

ab -n 10000 -c 100 "http://localhost:9502/async"
ab -n 10000 -c 100 "http://localhost:9502/sync"

經過測試,在后端接口響應性能有問題的情況下,swPromise 可以同時處理大量連接,用很低的 cpu 負載等待接口數據返回。

項目主頁:http://www.baiduhome.net/lib/view/home/1447936600338

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!