Swoole筆記(一)

本文示例代碼詳見:https://github.com/52fhy/swoole_demo

簡介

Swoole是一個PHP擴展,提供了PHP語言的異步多線程服務器,異步TCP/UDP網絡客戶端,異步MySQL,異步Redis,數據庫連接池,AsyncTask,消息隊列,毫秒定時器,異步文件讀寫,異步DNS查詢。 Swoole內置了Http/WebSocket服務器端/客戶端、Http2.0服務器端。

Swoole: PHP的異步、并行、高性能網絡通信引擎
http://www.swoole.com/

Github:
https://github.com/swoole
https://github.com/matyhtf

安裝

Swoole需要使用源碼安裝。暫無Windows版擴展。

源碼安裝

wget -O swoole.zip https://github.com/swoole/swoole-src/archive/v1.9.11.zip
unzip swoole.zip
cd swoole
phpize
./configure
make && make install

pecl安裝

由于pecl是需要編譯的,所以需要先安裝編譯器(已安裝編譯器可以忽略):

yum install -y gcc gcc-c++ make cmake bison autoconf

然后:

pecl install swoole
pecl install redis

pecl安裝擴展完成后會提示添加so文件到php.ini。示例:

Build process completed successfully
Installing '/usr/lib64/php/modules/swoole.so'
install ok: channel://pecl.php.net/swoole-1.9.11
configuration option "php_ini" is not set to php.ini location
You should add "extension=swoole.so" to php.ini

添加示例:

[swoole]
extension = /usr/lib64/php/modules/swoole.so

Swoole的進程模型

運行模式

Swoole目前總共有三種運行模式,默認為多進程模式(SWOOLE_PROCESS)。其中Base模式基本沒有生產應用價值;協程模式暫時還處于預覽階段;還有一種就是Swoole的多進程模式,也是官方目前最推薦用于生產環(huán)境的模式。

$serv = new swoole_server(string $host, int $port, int $mode = SWOOLE_PROCESS, int $sock_type = SWOOLE_SOCK_TCP);

實例分析

我們來使用實例進行分析:

<?php
$server = new \swoole_server("127.0.0.1",8088);//默認是多進程模式、TCP類型

$server->on('connect', function ($serv, $fd){ });
$server->on('receive', function ($serv, $fd, $from_id, $data){ });
$server->on('close', function ($serv, $fd){ });

$server -> start();

繼續(xù)在Shell中輸入以下命令:

php swoole_tcp_server.php
pstree -ap|grep swoole_tcp_server
  |   |       `-php,2454 swoole_tcp_server.php
  |   |           |-php,2456 swoole_tcp_server.php
  |   |           |   `-php,2458 swoole_tcp_server.php

從系統(tǒng)的輸出中,我們可以很容看出server其實有3個進程,進程的pid分別是2454、2456、2458,其中2454是2456的父進程,而2456又是2458的父進程。

所以,其實我們雖然看起來只是啟動了一個Server,其實最后產生的是三個進程。

這三個進程中,所有進程的根進程(2454),就是所謂的Master進程;而2456進程,則是Manager進程;最后的2458進程,是Worker進程。

基于此,我們簡單梳理一下,當執(zhí)行的start方法之后,發(fā)生了什么:

  • 守護進程模式下,當前進程fork出Master進程,然后退出,Master進程觸發(fā)OnMasterStart事件。
  • Master進程啟動成功之后,fork出Manager進程,并觸發(fā)OnManagerStart事件。
  • Manager進程啟動成功時候,fork出Worker進程,并觸發(fā)OnWorkerStart事件。

非守護進程模式下,則當前進程直接作為Master進程工作。

所以,一個最基礎的Swoole Server,至少需要有3個進程,分別是Master進程、Manager進程和Worker進程。

事實上,一個多進程模式下的Swoole Server中,有且只有一個Master進程;有且只有一個Manager進程;卻可以有n個Worker進程。

進程模型

Master進程是一個多線程進程,其中有一組非常重要的線程,叫做Reactor線程(組),每當一個客戶端連接上服務器的時候,都會由Master進程從已有的Reactor線程中,根據一定規(guī)則挑選一個,專門負責向這個客戶端提供維持鏈接、處理網絡IO與收發(fā)數據等服務。分包拆包等功能也是在這里完成。

Manager進程,某種意義上可以看做一個代理層,它本身并不直接處理業(yè)務,其主要工作是將Master進程中收到的數據轉交給Worker進程,或者將Worker進程中希望發(fā)給客戶端的數據轉交給Master進程進行發(fā)送。

Manager進程還負責監(jiān)控Worker進程,如果Worker進程因為某些意外掛了,Manager進程會重新拉起新的Worker進程,有點像Supervisor的工作。而這個特性,也是最終實現熱重載的核心機制。

Worker進程其實就是處理各種業(yè)務工作的進程,Manager將數據包轉交給Worker進程,然后Worker進程進行具體的處理,并根據實際情況將結果反饋給客戶端。

我們可以總結出來上面簡單的Server,當客戶端連接的時候這個過程中,三種進程之間是怎么協作的:

  1. Client主動Connect的時候,Client實際上是與Master進程中的某個Reactor線程發(fā)生了連接。
  2. 當TCP的三次握手成功了以后,由這個Reactor線程將連接成功的消息告訴Manager進程,再由Manager進程轉交給Worker進程。
  3. 在這個Worker進程中觸發(fā)了OnConnect的方法。
  4. 當Client向Server發(fā)送了一個數據包的時候,首先收到數據包的是Reactor線程,同時Reactor線程會完成組包,再將組好的包交給Manager進程,由Manager進程轉交給Worker。
  5. 此時Worker進程觸發(fā)OnReceive事件。
  6. 如果在Worker進程中做了什么處理,然后再用Send方法將數據發(fā)回給客戶端時,數據則會沿著這個路徑逆流而上。

Swoole進程/線程結構圖:


現在,我們基于上面的例子修改代碼,來看看一個簡單的多進程Swoole Server的幾個基本配置:

<?php
$server->set(array(
    'demonize' => false,//是否后臺運行
    'reactor_num' => 2,
    'worker_num' => 4
));

$server -> start();

reactor_num:表示Master進程中,Reactor線程總共開多少個,注意,這個可不是越多越好,因為計算機的CPU是有限的,所以一般設置為與CPU核心數量相同,或者兩倍即可。

worker_num:表示啟動多少個Worker進程,同樣,Worker進程數量不是越多越好,仍然設置為與CPU核心數量相同,或者兩倍即可。

我們可以在Shell里運行,使用pstree查看進程模型結構:

php swoole_tcp_server.php
pstree -ap|grep swoole_tcp
  |   |       `-php,2505 swoole_tcp_server.php
  |   |           |-php,2507 swoole_tcp_server.php
  |   |           |   |-php,2510 swoole_tcp_server.php
  |   |           |   |-php,2511 swoole_tcp_server.php
  |   |           |   |-php,2512 swoole_tcp_server.php
  |   |           |   `-php,2513 swoole_tcp_server.php

回調函數

Swoole作為Server時,回調函數有很多。但可以簡單分個類:

  1. 進程啟動時執(zhí)行的:onStart、onManagerStart、onWorkerStart;onWorkerStop、onManagerStop、onShutdown;onWorkerError
  2. 客戶端交互時觸發(fā)的:onReceive/onRequest/onPacket/onMessage、onOpen/onConnect、onClose
  3. Task:onTask、onFinish
  4. Timer:onTimer

事件執(zhí)行順序:

  • 所有事件回調均在$server->start后發(fā)生
  • 服務器關閉程序終止時最后一次事件是onShutdown
  • 服務器啟動成功后,onStart/onManagerStart/onWorkerStart會在不同的進程內并發(fā)執(zhí)行。
  • onReceive/onConnect/onClose/onTimer在worker進程(包括task進程)中各自觸發(fā)
  • worker/task進程啟動/結束時會分別調用onWorkerStart/onWorkerStop
  • onTask事件僅在task進程中發(fā)生
  • onFinish事件僅在worker進程中發(fā)生
  • onStart/onManagerStart/onWorkerStart 3個事件的執(zhí)行順序是不確定的
  • UDP協議下只有onReceive事件,沒有onConnect/onClose事件
  • 如果未設置onPacket回調函數,收到UDP數據包默認會回調onReceive函數
  • onOpen事件回調是可選的:當WebSocket客戶端與服務器建立連接并完成握手后會回調此函數

實際使用的時候不是所有回調都可以使用的,例如UDP服務器沒有onConnect/onClose;例如接收數據,在WebSocket里使用onReceive,在HttpServer使用onRequest,在UDPServer使用onPacket。

示例:

<?php
$server = new \swoole_server("127.0.0.1",8088);

$server->set(array(
    'daemonize' => false,
    'reactor_num' => 2,
    'worker_num' => 4
));

$server->on('connect', function ($serv, $fd){ 
    echo "client connect. fd is {$fd}\n";
});

$server->on('receive', function ($serv, $fd, $from_id, $data){
    echo "client connect. fd is {$fd}\n";
});

$server->on('close', function ($serv, $fd){
    echo "client close. fd is {$fd}\n";
});

// 以下回調發(fā)生在Master進程
$server->on("start", function (\swoole_server $server){
    echo "On master start.\n";
});
$server->on('shutdown', function (\swoole_server $server){
    echo "On master shutdown.\n";
});

// 以下回調發(fā)生在Manager進程
$server->on('ManagerStart', function (\swoole_server $server){
    echo "On manager start.\n";
});
$server->on('ManagerStop', function (\swoole_server $server){
    echo "On manager stop.\n";
});

// 以下回調也發(fā)生在Worker進程
$server->on('WorkerStart', function (\swoole_server $server, $worker_id){
    echo "Worker start\n";
});
$server->on('WorkerStop', function(\swoole_server $server, $worker_id){
    echo "Worker stop\n";
});
$server->on('WorkerError', function(\swoole_server $server, $worker_id, $worker_pid, $exit_code){
    echo "Worker error\n";
});

$server -> start();

原文發(fā)布在 http://www.cnblogs.com/52fhy/p/6931261.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,791評論 6 545
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 99,795評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,943評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,057評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,773評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,106評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,082評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,282評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 49,793評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,507評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,741評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,220評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,929評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,325評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,661評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,482評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,702評論 2 380

推薦閱讀更多精彩內容