swoole| swoole 協(xié)程知識點(diǎn)小結(jié)

date: 2019-04-30 23:11:23
title: swoole| swoole 協(xié)程知識點(diǎn)小結(jié)

swoole| swoole 協(xié)程知識點(diǎn)小結(jié)

本文要點(diǎn):

  • swoole 協(xié)程現(xiàn)狀一覽: 學(xué)不動? 其實(shí)是更簡單了
  • 使用 swoole 協(xié)程很簡單: 開個(gè)協(xié)程, 協(xié)程里寫非阻塞代碼
  • 展望 swoole 協(xié)程未來

swoole 協(xié)程現(xiàn)狀一覽

swoole 一直保持著 頗為快速 的迭代速度, 快到什么程度呢 -- 「快別更新了, 學(xué)不動了」

  • 半年前還是 v4.0 支持完整的協(xié)程編程(CSP, go+chan), 現(xiàn)在已經(jīng)迭代到 v4.3.3
  • v4.3 版本做了一次大更新, 項(xiàng)目拆分成了 swooleswoole_async
  • 官方 wiki 修改了很多, 協(xié)程的部分的文檔增加了不少, 而且提前到更加顯眼的地方

來一句靈魂叩問: 改動這么大, 那是不是真的 「學(xué)不動了」?

并不是! swoole 是一直在為 世界上最好的語言 添磚加瓦:

  • 更為完整的協(xié)程編程支持, 直觀的效果是更加 無縫無感 的編程切換體驗(yàn)(后面細(xì)說), 意味著需要了解和注意的語法細(xì)節(jié)更少, 編程更輕松
  • v4.3 版本做的一次大更新, 實(shí)際是優(yōu)化 swoole 項(xiàng)目的架構(gòu), 主項(xiàng)目 focus 協(xié)程模式的網(wǎng)絡(luò)編程, 更多網(wǎng)絡(luò)編程相關(guān)的功能, 使用 擴(kuò)展(ext) 的方式提供(具體可以參考 swoole 的 github 主頁: https://github.com/swoole, 掃一眼下面有的項(xiàng)目, 就能有所啟發(fā))
  • 官方 wiki 一直以信息量著稱(同時(shí)也意味著 可以學(xué)到很多東西), 但是如果具備了 網(wǎng)絡(luò)編程 + 協(xié)程 的基礎(chǔ)知識, 然后 focus 到 swoole 協(xié)程部分文檔 上, 就會發(fā)現(xiàn)其實(shí)都是一些 編程語法, so easy~

使用 swole 協(xié)程

如何使用協(xié)程:

  • 使用 go()(\Swoole\Coroutine::create() 的簡寫) 創(chuàng)建一個(gè)協(xié)程
  • 在 go() 的回調(diào)函數(shù)中, 加入?yún)f(xié)程需要執(zhí)行的代碼, 注意是 非阻塞代碼
use Swoole\Coroutine as Co; // 常用的縮寫方式

go(function () { // 創(chuàng)建協(xié)程, 回調(diào)函數(shù)中寫需要在協(xié)程中執(zhí)行的代碼
    echo "daydaygo";
    Co::sleep(1); // 不能是阻塞代碼
});

swoole 中協(xié)程就是這么簡單: 開個(gè)協(xié)程, 協(xié)程里寫非阻塞代碼. 官方協(xié)程部分文檔看起來很多, 牢記這兩點(diǎn), 其實(shí)很簡單!

開協(xié)程

  • 上文提到的, 使用 go() 創(chuàng)建一個(gè)協(xié)程
  • swoole server 中, 底層自動在 onRequet, onReceive, onConnect 等事件回調(diào)之前自動創(chuàng)建一個(gè)協(xié)程
  • 使用 task_enable_coroutine 開啟的協(xié)程版 Task 進(jìn)程, 會在 onTask 回調(diào)之前自動創(chuàng)建一個(gè)協(xié)程
  • 進(jìn)程和進(jìn)程池支持開啟協(xié)程, 開啟后創(chuàng)建的子進(jìn)程會自動創(chuàng)建協(xié)程
// tcp/udp server, 可以在此基礎(chǔ)可封裝 rpc
$s = new \Swoole\Server();

// http server, 替代傳統(tǒng)的 fpm
$s = new \Swoole\Http\Server();

// 開啟 http2 支持: https://wiki.swoole.com/wiki/page/326.html
$s = new \Swoole\Http\Server();
$s->set([
    'open_http2_protocol' => true,
]);
// 進(jìn)而可以實(shí)現(xiàn)基于 http2 的服務(wù), 比如 grpc

// websocket server
$s = new \Swoole\WebSocket\Server();

非阻塞代碼

協(xié)程中必須編寫 非阻塞代碼, 看到上面 Co::sleep(1) 的小伙伴會有疑問了:

連個(gè) sleep 都要 swoole 提供一個(gè)協(xié)程版, 我得掌握多少 swoole 協(xié)程版 API 才夠呀?

所以問題的關(guān)鍵點(diǎn)來了:

  • 協(xié)程中一定要使用 非阻塞代碼(一定要牢記, 多次重復(fù)了, 可以心里再默念三遍)
  • 怎么區(qū)分哪些是阻塞的, 哪些是非阻塞的: 可以參考 官方wiki - runtime
  • 隨著 swoole 的迭代, 對協(xié)程的支持越來越完整, 區(qū)分哪些阻塞, 哪些非阻塞, 越來越無感

swoole 更新后, 添加了開啟協(xié)程 runtime 功能:

// 沒有開啟協(xié)程 runtime, 需要協(xié)程版 API
use Swoole\Coroutine as Co;

go(function () {
    echo "daydaygo";
    Co::sleep(1); // 需要使用 swoole 提供的協(xié)程版 API
});

// 開啟協(xié)程 runtime
\Swoole\Runtime::enableCoroutine();
go(function () {
    echo "daydaygo";
    sleep(1); // 和原來編程一樣了
});

協(xié)程 runtime 開啟后, 支持的列表:

  • redis擴(kuò)展
  • 使用mysqlnd模式的pdo舌缤、mysqli擴(kuò)展匀奏,如果未啟用mysqlnd將不支持協(xié)程化
  • soap擴(kuò)展
  • file_get_contents、fopen
  • stream_socket_client (predis)
  • stream_socket_server
  • stream_select(需要4.3.2以上版本)
  • fsockopen
  • 文件操作 底層使用 AIO 線程池模擬實(shí)現(xiàn)
    • fopen / fclose
    • fread / fwrite
    • fgets / fputs
    • file_get_contents / file_put_contents
    • unlink / mkdir / rmdir
  • sleep系列函數(shù)
    • sleep / usleep
    • time_nanosleep / time_sleep_until

不支持的列表:

  • mysql:底層使用libmysqlclient
  • curl:底層使用libcurl (即不能使用CURL驅(qū)動的Guzzle)
  • mongo:底層使用mongo-c-client
  • pdo_pgsql / pdo_ori / pdo_odbc / pdo_firebird

協(xié)程 runtime 還不支持怎么辦

需要的功能協(xié)程 runtime 下還沒支持怎么辦? 你有三種方法:

  • 協(xié)程 runtime 之前, 官方和社區(qū)已經(jīng)貢獻(xiàn)了很多協(xié)程版 API 可供使用, 比如上面 Co::sleep(1), PostgreSQL Client Swoole\Coroutine\PostgreSQL
  • 官方和社區(qū)沒有, 可以使用 swoole 提供的協(xié)程版 client 進(jìn)行封裝, 可以參考 官方 amqp client 封裝, 將 socket() 函數(shù)實(shí)現(xiàn)的 tcp client, 使用 swoole 協(xié)程版 tcp client 實(shí)現(xiàn)即可
public function connect()
{
    // 使用 Swoole\Coroutine\Client 
    $sock = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP);
    if (!$sock->connect($this->host, $this->port, $this->connection_timeout))
    {
        throw new AMQPRuntimeException(
            sprintf(
                'Error Connecting to server(%s): %s ',
                $sock->errCode,
                swoole_strerror($sock->errCode)
            ),
            $sock->errCode
        );
    }
    $this->sock = $sock;
}

PS: 這里只是拋磚引玉, 原庫中使用 stream_socket_client(), 現(xiàn)在 swoole 協(xié)程 runtime 已經(jīng)支持了

  • 傳統(tǒng)的阻塞解決方案(當(dāng)然是在現(xiàn)有的協(xié)程方式都不行, 才會繼續(xù)使用傳統(tǒng)的方式): 拋給 swoole 的 task 進(jìn)程, 使用 MQ 異步掉, 等等

展望 swoole 協(xié)程的未來

到目前為止, 希望小伙伴們已經(jīng) get 到了 swoole 中協(xié)程編程的要點(diǎn)(我喜歡用 姿勢, 人最緊要的是姿勢好看~), 讓我們展望一下未來:

  • 解鎖更多協(xié)程使用: chan, defer, select, waitgroup, 這些官方都提供了 demo( 韓天峰 - PHP 協(xié)程:Go + Chan + Defer), 看完后自己也能封裝一份
  • 并不是 完整(100%, one hundred percent) 的支持, 要是一不小心踩到不支持的怎么辦? swoole 的后續(xù)版本將支持檢測協(xié)程環(huán)境下是否有阻塞調(diào)用
  • 隨著 swoole 官方在協(xié)程編程上的持續(xù)發(fā)力, 基于 swoole 實(shí)現(xiàn)的全協(xié)程式 PHP 開發(fā)框架也將更為簡單, 從基礎(chǔ)/底層的網(wǎng)絡(luò)編程到整個(gè)微服務(wù)架構(gòu)的道路也將更為平坦, 比如馬上將要迎來大版本升級的 swoft2

寫在最后

官方協(xié)程部分的文檔看起來多, 其實(shí)多是對協(xié)程 API 的介紹, 并沒有在知識結(jié)構(gòu)理解的復(fù)雜度上有所增加. swoole 中協(xié)程的編程語法, 都在 \Swoole\Coroutine 命名空間下可以找到.

最后回到一個(gè)經(jīng)典問題, 學(xué)習(xí) swoole 的協(xié)程好, 還是學(xué)習(xí) go 的協(xié)程好? 我談?wù)勎覀€(gè)人的觀點(diǎn):

  • 所需要的基礎(chǔ)知識: 網(wǎng)絡(luò)編程 + 協(xié)程, 不會因?yàn)槟闶怯?swoole 還是 go 而有所減少, 基礎(chǔ)不大好, 表現(xiàn)出來了就是學(xué)著學(xué)著就容易卡住, 效率上不來
  • 以為你寫的是 swoole, 不不不, 寫的是一個(gè)又一個(gè)功能的 API, go 也同樣(要用到 redis/mysql/mq, 相應(yīng)的 API 你還是得學(xué)得會), 區(qū)別在于, swoole 趨勢是在底層實(shí)現(xiàn)支持(比如 協(xié)程runtime), 這樣 PHPer 可以無縫切換過來, 而 Gopher 則需要學(xué)習(xí)一個(gè)又一個(gè)基于 go 協(xié)程封裝好的 API. 當(dāng)初在 PHP 中學(xué)習(xí)的這些 API, 到 go 里面, 一樣需要再熟悉一遍
  • 最后來談?wù)勑阅? 請?jiān)试S我用一個(gè)傲嬌一點(diǎn)的說, 你用 swoole 達(dá)不到的性能, 換個(gè)語言, 呵呵呵. 難易程度排行: 加機(jī)器 < 加程序員 < 加語言.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市盛杰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蕴茴,老刑警劉巖十嘿,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異屈嗤,居然都是意外死亡潘拨,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進(jìn)店門饶号,熙熙樓的掌柜王于貴愁眉苦臉地迎上來铁追,“玉大人,你說我怎么就攤上這事茫船±攀” “怎么了?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵算谈,是天一觀的道長涩禀。 經(jīng)常有香客問我,道長然眼,這世上最難降的妖魔是什么艾船? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮高每,結(jié)果婚禮上屿岂,老公的妹妹穿的比我還像新娘。我一直安慰自己鲸匿,他們只是感情好雁社,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著晒骇,像睡著了一般霉撵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上洪囤,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天徒坡,我揣著相機(jī)與錄音,去河邊找鬼瘤缩。 笑死喇完,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的剥啤。 我是一名探鬼主播锦溪,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼不脯,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了刻诊?” 一聲冷哼從身側(cè)響起防楷,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎则涯,沒想到半個(gè)月后复局,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡粟判,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年亿昏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片档礁。...
    茶點(diǎn)故事閱讀 38,605評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡角钩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出呻澜,到底是詐尸還是另有隱情彤断,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布易迹,位于F島的核電站宰衙,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏睹欲。R本人自食惡果不足惜供炼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望窘疮。 院中可真熱鬧袋哼,春花似錦、人聲如沸闸衫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蔚出。三九已至弟翘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間骄酗,已是汗流浹背稀余。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留趋翻,地道東北人睛琳。 一個(gè)月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親师骗。 傳聞我的和親對象是個(gè)殘疾皇子历等,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評論 2 348

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