協程

協程是一種比線程更輕量級的用戶級並發機制,能夠在進程中實現多任務調度。它透過手動控制掛起和恢復來實現協程間的切換,避免了進程上下文切換的開銷。
workerman提供了一個通用的協程接口,底層自動兼容Swoole/Swow/Fiber驅動。

提示
此特性需要 workerman>=5.1.0

注意

  • 協程僅支持 Swoole Swow Fiber 驅動
  • 如果使用 Fiber 驅動時需要安裝 composer require revolt/event-loop
  • Swoole 或者 Swow 驅動可以實現 PHP 阻塞函數自動協程化,從而實現原來的同步代碼異步執行
  • Fiber 無法像 SwooleSwow 那樣自動協程化,遇到 PHP 自帶的阻塞函數時會阻塞整個進程,並不會發生協程切換
  • 當使用 Swoole Swow Fiber 驅動時,workerman 每次運行 onWorkerStart onMessage onConnect onClose 等回調時會自動創建一個協程來執行
  • 可以利用 $worker->eventLoop=xxx; 給不同 worker 設置不同的協程驅動
<?php
use Workerman\Connection\TcpConnection;
use Workerman\Coroutine;
use Workerman\Events\Swoole;
use Workerman\Events\Fiber;
use Workerman\Protocols\Http\Request;
use Workerman\Worker;
require_once __DIR__ . '/vendor/autoload.php';

$worker1 = new Worker('http://0.0.0.0:8001');
$worker1->eventLoop = Swoole::class; // 使用Swoole協程
$worker1->onMessage = function (TcpConnection $connection, Request $request) {
    Coroutine::create(function () {
        echo file_get_contents("http://www.example.com/event/notify");
    });
    $connection->send('ok');
};

$worker2 = new Worker('http://0.0.0.0:8001');
$worker2->eventLoop = Fiber::class; // 使用自帶的Fiber協程
$worker2->onMessage = function (TcpConnection $connection, Request $request) {
    Coroutine::create(function () {
        echo file_get_contents("http://www.example.com/event/notify");
    });
    $connection->send('ok');
};

Worker::runAll();

協程提供的接口

interface CoroutineInterface
{

    /**
     * 創建協程並立即執行
     */
    public static function create(callable $callable, ...$data): CoroutineInterface;

    /**
     * 開始協程運行
     */
    public function start(mixed ...$args): mixed;

    /**
     * 恢復協程運行
     */
    public function resume(mixed ...$args): mixed;

    /**
     * 獲取協程id
     */
    public function id(): int;

    /**
     * 設置協程銷毀時的回調
     */
    public static function defer(callable $callable): void;

    /**
     * 暫停當前協程
     */
    public static function suspend(mixed $value = null): mixed;

    /**
     * 獲取當前協程
     */
    public static function getCurrent(): CoroutineInterface|Fiber|SwowCoroutine|static;

    /**
     * 判斷當前是否是協程環境
     */
    public static function isCoroutine(): bool;

}

關於協程

優勢

PHP引入協程後最大的作用就是可以用同步的方式編寫異步代碼,避免了回調地獄,提高了代碼的可讀性和可維護性。
協程能大幅度提升IO密集型業務的彈性,可以用較少的進程提供更大的吞吐量。

劣勢

但是引入協程後開發者需要時刻注意全局變量污染、資源競爭、第三方庫改造等問題,開發維護成本增大,心智負擔明顯增加。

引入協程後產生了協程創建、調度、銷毀、連接池等額外開銷。
通過大量壓測數據來看,在充分利用CPU的情況下,引入協程後極限性能比阻塞式IO下降約10%-20%。