Swoole-2.0.1-Alpha 已釋出,提供PHP原生協程支援
阿新 • • 發佈:2022-04-28
Swoole-2.0 提供了PHP原生協程排程器,PHP程式碼可以按照同步方式編寫,底層引擎使用非同步IO,排程器會在IO完成後自動切換PHP函式呼叫棧。
內建協程不依賴PHP的Yield/Generator語法,實現了真正的同步程式碼,非同步IO。Swoole-2.0相容Swoole-1.0所有現存特性,同時支援同步阻塞、非同步非阻塞回調、協程 3 種 IO 模型。
協程可以理解為使用者態執行緒,通過協作而不是搶佔來進行切換。相對於作業系統程序或者執行緒,協程所有的操作都可以在使用者態完成,建立和切換的消耗更低。Swoole可以為每一個請求建立對應的協程,根據IO的狀態來合理地排程協程。
Swoole-2.0內建協程的優勢:
- 開發者可以使用最傳統的PHP同步編碼方式即可實現非同步IO。過去使用Node.js和Swoole1.0編寫非同步程式時必須使用巢狀回撥,開發效率低,邏輯複雜之後開發維護都很困難。現在使用Swoole-2.0內建協程可以輕鬆編寫非同步程式,徹底告別
Callback Hell
。 - 由於Swoole-2.0是在底層封裝了協程,所以對比其他的PHP協程框架,如TSF、Zan,開發者不需要額外使用yield關鍵詞來標識一個協程IO切換操作,所以不再需要對yield的語義進行深入理解以及對每一級的呼叫都修改為
yield
,這極大的提高了開發效率。 - Swoole-2.0除了支援常見的如TCP、UDP、HTTP、Redis、MySQL協程客戶端之外,還支援併發Client,可以同時併發執行多個不同型別的協程客戶端呼叫,時間為Max(耗時最長Client的時間)。而Go語言需要使用非常複雜的
goroutine
chan
+select
才能實現。Swoole-2.0的併發呼叫使用更加簡單。
主要特性:
- 底層內建協程,同步的程式碼,非同步的IO
- TCP/UDP/UnixSocket協程客戶端
SwooleCoroutineClient
- Http/WebSocket協程客戶端
SwooleCoroutineHttpClient
- Redis協程客戶端
SwooleCoroutineRedis
- MySQL協程客戶端
SwooleCoroutineMySQL
- 併發Client
- 支援非同步超時
- 預設開啟名稱空間
要求:
- 需要PHP5.5或更高版本
- 暫時不支援PHP7
- 伺服器端基於
SwooleServer
或者SwooleHttpServer
onRequet
、onReceive
、onConnect
回撥中使用協程
安裝方式:
Swoole-2.0需要通過新增--enable-coroutine
編譯引數啟用協程能力,示例如下:
phpize./configure --with-php-config={path-to-php-config} --enable-coroutinemake
make install
新增編譯引數,swoole server將切換到協程模式
使用示例
TCP協程客戶端
$client = new SwooleCoroutineClient(SWOOLE_SOCK_TCP);$client->connect('127.0.0.1', 9501,0.5)//呼叫connect將觸發協程切換$client->send("hello worldn");
echo $client->recv();//呼叫recv將再次觸發協程切換$client->close();
Http協程客戶端
$cli = new SwooleCoroutineHttpClient('127.0.0.1', 80);$cli->setHeaders([ 'Host' => "localhost", "User-Agent" => 'Chrome/49.0.2587.3', 'Accept' => 'text/html,application/xhtml+xml,application/xml', 'Accept-Encoding' => 'gzip',
]);$cli->set([ 'timeout' => 1]);$cli->get('/index.php');echo $cli->body;$cli->close();
Redis協程客戶端
$redis = new SwooleCoroutineRedis();$redis->connect('127.0.0.1', 6379);$val = $redis->get('key');
MySQL協程客戶端
$swoole_mysql = new SwooleCoroutineMySQL();$swoole_mysql->connect(['host' => '127.0.0.1',
'user' => 'user', 'password' => 'pass',
'database' => 'test',
]);$res = $swoole_mysql->query('select sleep(1)');
併發呼叫
$tcpclient = new SwooleCoroutineClient(SWOOLE_SOCK_TCP);$tcpclient->connect('127.0.0.1', 9501,0.5)$tcpclient->send("hello worldn");$redis = new SwooleCoroutineRedis();$redis->connect('127.0.0.1', 6379);$redis->setDefer();$redis->get('key');$mysql = new SwooleCoroutineMySQL();$mysql->connect(['host' => '127.0.0.1', 'user' => 'user', 'password' => 'pass', 'database' => 'test',
]);$mysql->setDefer();$mysql->query('select sleep(1)');$httpclient = new SwooleCoroutineHttpClient('0.0.0.0', 9599);$httpclient->setHeaders(['Host' => "api.mp.qq.com"]);$httpclient->set([ 'timeout' => 1]);$httpclient->setDefer();$httpclient->get('/');$tcp_res = $tcpclient->recv();$redis_res = $redis->recv();$mysql_res = $mysql->recv();$http_res = $httpclient->recv();
通常,如果一個業務請求中需要做一次redis請求和一次mysql請求,那麼網路IO會是這樣:
redis發包->redis收包->mysql發包->mysql收包
以上流程網路IO的時間就等於 redis網路IO時間 + mysql網路IO時間。
而對於協程版本的Client,網路IO可以是這樣:
redis發包->mysql發包->redis收包->mysql收包
以上流程網路IO的時間就接近於 MAX(redis網路IO時間, mysql網路IO時間)。
現在支援併發請求的Client有:
- SwooleCoroutineClient
- SwooleCoroutineRedis
- SwooleCoroutineMySQL
- SwooleCoroutineHttpClient
除了SwooleCoroutineClient
,其他Client都實現了defer特性,用於宣告延遲收包。
注意事項
- 全域性變數:協程使得原有的非同步邏輯同步化,但是在協程的切換是隱式發生的,所以在協程切換的前後不能保證全域性變數以及static變數的一致性。
- 請勿在下列場景中呼叫協程客戶端:
- 解構函式
__destruct()
- 魔術方法
__call()
- 解構函式
- 當前版本為Alpha預覽版本,不建議在生產環境上使用