1. 程式人生 > 其它 >Swoole-2.0.1-Alpha 已釋出,提供PHP原生協程支援

Swoole-2.0.1-Alpha 已釋出,提供PHP原生協程支援

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內建協程的優勢:

  1. 開發者可以使用最傳統的PHP同步編碼方式即可實現非同步IO。過去使用Node.js和Swoole1.0編寫非同步程式時必須使用巢狀回撥,開發效率低,邏輯複雜之後開發維護都很困難。現在使用Swoole-2.0內建協程可以輕鬆編寫非同步程式,徹底告別Callback Hell
  2. 由於Swoole-2.0是在底層封裝了協程,所以對比其他的PHP協程框架,如TSFZan,開發者不需要額外使用yield關鍵詞來標識一個協程IO切換操作,所以不再需要對yield的語義進行深入理解以及對每一級的呼叫都修改為yield,這極大的提高了開發效率。
  3. 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
    進行開發,目前僅支援在onRequetonReceiveonConnect回撥中使用協程

安裝方式:

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特性,用於宣告延遲收包。

注意事項

  1. 全域性變數:協程使得原有的非同步邏輯同步化,但是在協程的切換是隱式發生的,所以在協程切換的前後不能保證全域性變數以及static變數的一致性。
  2. 請勿在下列場景中呼叫協程客戶端:
    • 解構函式__destruct()
    • 魔術方法__call()
  3. 當前版本為Alpha預覽版本,不建議在生產環境上使用

使用新版本