코루틴

코루틴은 스레드보다 더 경량화된 사용자 수준의 병행성 메커니즘으로, 프로세스 내에서 다중 작업 스케줄링을 구현할 수 있습니다. 코루틴 간의 전환은 수동으로 일시 중지 및 복원을 제어하여 이루어지며, 프로세스 컨텍스트 스위칭의 오버헤드를 피할 수 있습니다.
workerman은 하위에서 자동으로 Swoole/Swow/Fiber 드라이버와 호환되는 범용 코루틴 인터페이스를 제공합니다.


이 기능은 workerman>=5.1.0이 필요합니다.

주의

  • 코루틴은 Swoole Swow Fiber 드라이버만 지원합니다.
  • Fiber 드라이버를 사용할 경우 composer require revolt/event-loop을 설치해야 합니다.
  • Swoole 또는 Swow 드라이버는 PHP 블로킹 함수 자동 코루틴화를 통해 기존의 동기 코드를 비동기적으로 실행할 수 있습니다.
  • 그러나 FiberSwooleSwow처럼 자동으로 코루틴화할 수 없으며, 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% 감소합니다.