[原創(chuàng)]Swoole和Swoft的那些事 (Http/Rpc服務(wù)篇)

Swoft在PHPer圈中是一個(gè)門檻較高的Web框架俐镐,不僅僅由于框架本身帶來(lái)了很多新概念和前沿的設(shè)計(jì)启搂,還在于Swoft是一個(gè)基于Swoole的框架。Swoole在PHPer圈內(nèi)學(xué)習(xí)成本最高的工具沒(méi)有之一羡鸥,雖然Swoft的出現(xiàn)降低了Swoole的使用成本曾沈,但如果你對(duì)Swoole本身了解不夠深入,仍然很難避免栽進(jìn)種種"坑"中烙博。

考慮到這個(gè)現(xiàn)狀瑟蜈,也為降低閱讀難度烟逊,后續(xù)幾個(gè)和Swoole聯(lián)系較為密切的機(jī)制,筆者會(huì)調(diào)整寫作思路铺根,將文章的定位從 「幫助讀者深入理解Swoft」 調(diào)整為 「幫助讀者理解Swoft和Swoole」宪躯,敘述節(jié)奏也會(huì)放慢。

三種PHP應(yīng)用的Web模型

LNMP模型

LNMP和LAMP是絕大多數(shù)PHPer最熟悉的基礎(chǔ)Web架構(gòu)位迂,這里以常見(jiàn)的LNMP作為例子描述一個(gè)常見(jiàn) 無(wú)Swoole應(yīng)用的構(gòu)件組成:Nginx充當(dāng)Webservice,PHP-fpm維護(hù)一個(gè)進(jìn)程池去運(yùn)行Web項(xiàng)目访雪。

對(duì)比更古老的cgi模型,php-fpm已經(jīng)引入了進(jìn)程常駐的概念,避免每次請(qǐng)求創(chuàng)建并銷毀進(jìn)程的開(kāi)銷以及拓展加載的開(kāi)銷掂林,但是每個(gè)請(qǐng)求仍然要執(zhí)行PHP RINITRSHUTDOWN 之間的所有流程臣缀,包括重新加載一次框架源碼以及項(xiàng)目代碼,造成極大的性能浪費(fèi)泻帮。

這種模型的優(yōu)點(diǎn)是簡(jiǎn)單成熟和穩(wěn)定精置,一次運(yùn)行隨后銷毀 帶來(lái)的開(kāi)發(fā)便捷性是PHP能夠流行起來(lái)的原因之一。市面上絕大多數(shù)PHP項(xiàng)目使用的都是基于該種架構(gòu)的變體锣杂。

LNMP-with-Swoole模型

LNMP-with-Swoole 是 LNMP的一種變體脂倦,其在LNMP的基礎(chǔ)上引入了Swoole組件。

和PHP-fpm一樣蹲堂,Swoole有一套自己的進(jìn)程管理機(jī)制狼讨。但由于代碼變得高度常駐和編程思維需要從同步到異步的轉(zhuǎn)變贝淤,所以Swoole和傳統(tǒng)的基于PHP-fpm的Web框架親和度很低,即使是適配升級(jí)過(guò)的老式Web框架柒竞,目前在Swoole上運(yùn)行的表現(xiàn)往往并不好。

因此出現(xiàn)了這在這種折中方案播聪,并沒(méi)有直接將原有PHP代碼運(yùn)行在Swoole中朽基,而是使用Swoole搭建了一個(gè)服務(wù),系統(tǒng)通過(guò)接口與Swoole通信离陶,從而為Web項(xiàng)目補(bǔ)充了異步處理的能力稼虎。我稱呼這種同時(shí)使用PHP-fpm和Swoole的系統(tǒng)為 半Swoole應(yīng)用。因?yàn)榻尤牒?jiǎn)單招刨,所以是絕大多數(shù)現(xiàn)有項(xiàng)目?jī)?yōu)先考慮的Swoole接入方案霎俩。

LNMP-with-Swoole模型雖然引入了Swoole和異步處理能力,但是核心還是php-fpm沉眶,實(shí)際上還遠(yuǎn)遠(yuǎn)沒(méi)有發(fā)揮出Swoole的真正優(yōu)勢(shì)打却。

Swoole-HTTP-Server模型

Swoole-HTTP-Server和LNMP-with-Swoole相比有巨大的變化,這種模型中充當(dāng)WebServer角色的構(gòu)件不僅僅有nginx,應(yīng)用本身也包含了一個(gè)內(nèi)建WebServer,不過(guò)由于Swoole Http Server不是專業(yè)的Http Server谎倔,對(duì)Http的處理不完善 柳击,因此仍然需要使用Nginx作為靜態(tài)資源服務(wù)器以及反代,Swoole Http Server僅僅處理PHP相關(guān)的Http流量片习。

一方面由于Swoole已經(jīng)包含了WebServer捌肴,不再需要實(shí)現(xiàn)cgi或者fast-cgi的通用協(xié)議去和WebServer通信蹬叭,另一方面Swoole有自己的進(jìn)程管理,因此PHP-fpm可以直接被去除了状知。對(duì)于PHP資源而言秽五,在這種模型中,Swoole Http Server的地位相當(dāng)于傳統(tǒng)模型中的nginx和PHP-fpm之和。

一次加載常駐內(nèi)存试幽,不同的請(qǐng)求間基本上復(fù)用了onRequest以外的所有流程筝蚕,使得每個(gè)請(qǐng)求的開(kāi)銷大大降低;異步IO的特性使得這種模型吞吐量遠(yuǎn)遠(yuǎn)高于傳統(tǒng)的LNMP模型铺坞。另外相對(duì)于獨(dú)立的Swoole服務(wù)起宽,內(nèi)嵌在Web系統(tǒng)中的Swoole使用更加的直接方便,支持更好济榨。

Swoft和Swoole的關(guān)系是什么?

  1. Swoole是一個(gè)異步引擎,核心是為PHP提供異步IO執(zhí)行的能力坯沪,同時(shí)提供一套異步編程可能會(huì)用到的工具集。
  2. Swoole-HTTP-Server是Swoole的一個(gè)組件擒滑,是SwooleServer中的一種腐晾,提供了一個(gè)適合Swoole直接運(yùn)行的HttpServer環(huán)境。
  3. Swoft一個(gè)現(xiàn)代的Web框架丐一,和Swoole親和性高藻糖,同時(shí)也是上面提到的Swoole-HTTP-Server模型模型的一個(gè)實(shí)踐。

Swoft管理著該Web模型中的Swoole,以及Swoole-Http-server库车,對(duì)開(kāi)發(fā)者屏蔽swoole的種種復(fù)雜操作細(xì)節(jié)巨柒,并作為一個(gè)Web框架向開(kāi)發(fā)者提供各種Web開(kāi)發(fā)需要用到的路由,MVC柠衍,數(shù)據(jù)庫(kù)訪問(wèn)等功能組件等洋满。

Swoft是如何使用Swoole的?

最核心的就是HttpServerr以及RpcServer

Http服務(wù)器

Swoft直接使用的是Swoole內(nèi)建的\Swoole\Http\Server,它已經(jīng)處理好所有Http層面的所有東西珍坊,我們只需要關(guān)注應(yīng)用本身,我們來(lái)看一下Http服務(wù)幾個(gè)重要生命周期點(diǎn)牺勾。

swoole啟動(dòng)前

這個(gè)階段進(jìn)行的行為有幾個(gè)特征
1.基礎(chǔ)bootstrap行為:如必須的常量define,composer加載器引入阵漏,配置讀取驻民。
2.需要生成被所有worker/task進(jìn)程共享的程序全局期的對(duì)象,如Swoole\Lock,Swoft\Memory\Table的創(chuàng)建。
3.啟動(dòng)時(shí)履怯,所有進(jìn)程中合計(jì)只能執(zhí)行一次的操作:如前置Process的啟動(dòng)回还。
4.Bean容器基本初始化,以及項(xiàng)目啟動(dòng)流程需要的coreBean的加載虑乖。
這塊涉及東西比較雜懦趋,為控制篇幅后續(xù)用單獨(dú)文章介紹。

和Http服務(wù)關(guān)系最密切的進(jìn)程是Swoole中的Worker進(jìn)程(組)疹味,絕大部分業(yè)務(wù)處理都在該進(jìn)程中進(jìn)行仅叫。
對(duì)于每個(gè)Swoole事件帜篇,Swoft都提供了對(duì)應(yīng)的Swoole監(jiān)聽(tīng)器(對(duì)應(yīng)@SwooleListener注解)作為事件機(jī)制的封裝。要理解Swoft的HttpServer是如何在Swoole下運(yùn)行的我們重點(diǎn)需要關(guān)注下兩個(gè)在兩個(gè)Swoole事件swoole.workerStartswoole.onRequest诫咱。

swoole.workerStart事件

workerStart事件在TaskWorker/Worker進(jìn)程啟動(dòng)時(shí)發(fā)生笙隙,每個(gè)TaskWorker/Worker進(jìn)程里都會(huì)執(zhí)行一次。
這是個(gè)關(guān)鍵節(jié)點(diǎn)坎缭,因?yàn)?code>swoole.workerStart回調(diào)之后新建的對(duì)象都是進(jìn)程全局期的竟痰,使用的內(nèi)存都屬于特定的Task/Worker進(jìn)程,相互獨(dú)立掏呼。也只有在這個(gè)階段或以后初始化的部分才是可以被熱重載的坏快。
事件底層關(guān)鍵代碼如下:

Swoft\Bootstrap\Server\ServerTrait.php
    /**
     * @param bool $isWorker
     * @throws \InvalidArgumentException
     * @throws \ReflectionException
     */
    protected function reloadBean(bool $isWorker)
    {
        BeanFactory::reload();
        $initApplicationContext = new InitApplicationContext();
        $initApplicationContext->init();

        if($isWorker && $this->workerLock->trylock() && env('AUTO_REGISTER', false)){
            App::trigger(AppEvent::WORKER_START);
        }
    }

這里做的事情有3點(diǎn)

  1. 初始化Bean容器:
    上文中的BeanFactory::reload();就是Swoft的Bean容器初始化入口,注解的掃描也是在此處進(jìn)行(實(shí)際上這個(gè)說(shuō)法并不準(zhǔn)確憎夷,Bean容器真正的初始化階段在Swoole Server啟動(dòng)前的BootStrap階段就已經(jīng)進(jìn)行了莽鸿,只不過(guò)那時(shí)進(jìn)行的是少部分初始化,相對(duì)swoole.workerStart中的初始化的Bean數(shù)量拾给,比重很邢榈谩)。在workerStart中初始化Bean容器是Swoft可以熱更新代碼的基礎(chǔ)蒋得。
  2. 初始化的應(yīng)用上下文
    initApplicationContext->init()會(huì)注冊(cè)Swoft事件監(jiān)聽(tīng)器(對(duì)應(yīng)@Listener),方便用戶處理Swoft應(yīng)用本身的各種鉤子级及。隨后觸發(fā)一個(gè)swoft.applicationLoader事件,各組件通過(guò)該事件進(jìn)行配置文件加載,http/rpc路由注冊(cè)。
  3. 服務(wù)注冊(cè)
    具體內(nèi)容會(huì)在服務(wù)治理章節(jié)講述额衙。

swoole.onRequest事件

每個(gè)http請(qǐng)求到來(lái)時(shí)僅僅會(huì)觸發(fā)swoole.onRequest事件饮焦。
框架代碼本身都是由大量進(jìn)程全局期和少量程序全局期的對(duì)象構(gòu)成,而onReceive中創(chuàng)建的對(duì)象譬如$request$response都是請(qǐng)求期的,隨著http請(qǐng)求的結(jié)束而回收入偷。
事件底層關(guān)鍵代碼如下:

    /**
     * Do dispatcher
     *
     * @param array ...$params
     * @return \Psr\Http\Message\ResponseInterface
     * @throws \InvalidArgumentException
     */
    public function dispatch(...$params): ResponseInterface
    {
        /**
         * @var RequestInterface $request
         * @var ResponseInterface $response
         */
        list($request, $response) = $params;

        try {
            // before dispatcher
            $this->beforeDispatch($request, $response);

            // request middlewares
            $middlewares = $this->requestMiddleware();
            $request = RequestContext::getRequest();
            $requestHandler = new RequestHandler($middlewares, $this->handlerAdapter);
            $response = $requestHandler->handle($request);

        } catch (\Throwable $throwable) {
            /* @var ErrorHandler $errorHandler */
            $errorHandler = App::getBean(ErrorHandler::class);
            $response = $errorHandler->handle($throwable);
        }

        $this->afterDispatch($response);

        return $response;
    }
  1. beforeDispatch($request, $response):
    設(shè)置請(qǐng)求上下文追驴,并觸發(fā)一個(gè)swoft.beforeRequest事件械哟。
  2. RequestHandler->handle($request):
    執(zhí)行各個(gè) 中間件 和請(qǐng)求對(duì)應(yīng)的 action疏之,具體處理可以參考RPC章節(jié),原理基本相同暇咆。
  3. $afterDispatch($response):
    整理http響應(yīng)報(bào)文發(fā)送客戶端并觸發(fā)swoft.resourceRelease(詳情在連接池一文中提及)事件和swoft.afterRequest事件

總的來(lái)說(shuō)锋爪,縱觀這幾個(gè)生命周期點(diǎn)你需要搞清楚幾件事:

  1. Swoole的worker進(jìn)程是你絕大多數(shù)Http服務(wù)代碼的運(yùn)行環(huán)境。
  2. 一部分初始化和加載操作在swoole的server啟動(dòng)前完成爸业,一部分在swoole.workerStart事件回調(diào)中完成其骄,前者無(wú)法熱重載但可能被多個(gè)進(jìn)程共享。
  3. 初始化代碼只會(huì)在系統(tǒng)啟動(dòng)和Worker/Task進(jìn)程啟動(dòng)時(shí)執(zhí)行一次扯旷, 不像PHP-fpm每次請(qǐng)求都會(huì)執(zhí)行一次拯爽,框架對(duì)象也不像PHP-fpm會(huì)隨請(qǐng)求返回而銷毀。
  4. 每次請(qǐng)求都會(huì)觸發(fā)一次swoole.onRequest事件钧忽,里面就是我們的請(qǐng)求處理代碼真正運(yùn)行的地方毯炮,只有這事件內(nèi)產(chǎn)生的對(duì)象才會(huì)在請(qǐng)求結(jié)束時(shí)被回收逼肯。

RPC服務(wù)器

生命周期和Http服務(wù)基本一致,詳情參考《[原創(chuàng)]Swoft源碼剖析-RPC功能實(shí)現(xiàn)》

Swoft源碼剖析系列目錄:http://www.reibang.com/p/2f679e0b4d58

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末桃煎,一起剝皮案震驚了整個(gè)濱河市篮幢,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌为迈,老刑警劉巖三椿,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異葫辐,居然都是意外死亡搜锰,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門耿战,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)纽乱,“玉大人,你說(shuō)我怎么就攤上這事昆箕⊙涣校” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵鹏倘,是天一觀的道長(zhǎng)薯嗤。 經(jīng)常有香客問(wèn)我,道長(zhǎng)纤泵,這世上最難降的妖魔是什么骆姐? 我笑而不...
    開(kāi)封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮捏题,結(jié)果婚禮上玻褪,老公的妹妹穿的比我還像新娘。我一直安慰自己公荧,他們只是感情好带射,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著循狰,像睡著了一般窟社。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上绪钥,一...
    開(kāi)封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天灿里,我揣著相機(jī)與錄音,去河邊找鬼程腹。 笑死匣吊,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播色鸳,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼侣灶,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了缕碎?” 一聲冷哼從身側(cè)響起褥影,我...
    開(kāi)封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎咏雌,沒(méi)想到半個(gè)月后凡怎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赊抖,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年统倒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片氛雪。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡房匆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出报亩,到底是詐尸還是另有隱情浴鸿,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布弦追,位于F島的核電站岳链,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏劲件。R本人自食惡果不足惜掸哑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望零远。 院中可真熱鬧苗分,春花似錦、人聲如沸牵辣。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)服猪。三九已至供填,卻和暖如春拐云,著一層夾襖步出監(jiān)牢的瞬間罢猪,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工叉瘩, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留膳帕,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像危彩,于是被迫代替她去往敵國(guó)和親攒磨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 1. Nginx的模塊與工作原理 Nginx由內(nèi)核和模塊組成汤徽,其中娩缰,內(nèi)核的設(shè)計(jì)非常微小和簡(jiǎn)潔,完成的工作也非常簡(jiǎn)單...
    rosekissyou閱讀 10,221評(píng)論 5 124
  • 原文github地址 1.PHP概述 1.1 PHP的歷史發(fā)展 1995年由Lerdorf創(chuàng)建PHP谒府,高級(jí)腳本語(yǔ)言...
    10xjzheng閱讀 1,521評(píng)論 0 2
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理拼坎,服務(wù)發(fā)現(xiàn),斷路器完疫,智...
    卡卡羅2017閱讀 134,701評(píng)論 18 139
  • 我不知道我能否用我的筆描繪出七星湖那如夢(mèng)幻般的畫卷泰鸡,它的如詩(shī)般的美麗清雅確實(shí)迷醉了我。它的美是一種潔凈壳鹤、超然盛龄、輕靈...
    暖風(fēng)清揚(yáng)閱讀 334評(píng)論 0 0
  • 和前任分手五年了余舶,這期間我一直單身,不是沒(méi)有追過(guò)自己的男生锹淌,而是自己想等一份自己想要的愛(ài)情欧芽,哪怕我也不知道自己想要...
    泡桐姑娘閱讀 412評(píng)論 0 3