PHP Academy · 강의

Swoole을 활용한 고성능 서버

코루틴 기반 Swoole로 수천 개의 연결을 처리합니다.

레슨 4/413개 단계

Swoole을 활용한 고성능 서버은(는) CoddyKit의 무료 PHP Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 PHP Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. PHP Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

Swoole이란

Swoole(및 그 포크인 OpenSwoole)은 PHP를 고성능 코루틴 기반 이벤트 중심 런타임으로 바꾸는 C 확장입니다. PHP-FPM이 요청마다 프로세스를 생성하는 대신, Swoole 서버는 애플리케이션을 한 번만 부팅하고 메모리에 상주시키며, 코루틴이 내장된 장수 작업자 풀에서 요청을 처리합니다. 그 결과 처리량이 몇 자릿수 규모로 향상되고, 단일 요청 안에서도 네이티브 동시성을 사용할 수 있습니다.

지속형 HTTP 서버

전형적인 예는 스크립트 하나로 구현한 완전한 HTTP 서버입니다. 서버는 요청을 수신하고 각 요청을 작업자에게 전달하며, 프레임워크를 다시 부트스트랩하지 않습니다. 이 기능에는 ext-swoole이 필요하며 기본 PHP CLI에서는 실행되지 않습니다.

<?php
$server = new Swoole\Http\Server('0.0.0.0', 9501);
$server->set(['worker_num' => 4]);

$server->on('request', function ($request, $response) {
    $response->header('Content-Type', 'application/json');
    $response->end(json_encode(['hello' => $request->server['request_uri']]));
});

echo "listening on :9501\n";
$server->start();

콜백이 아닌 코루틴

Swoole의 핵심 기능은 코루틴입니다. 런타임이 협력적으로 스케줄링하는 차단 방식의 순차 코드를 작성할 수 있습니다. Coroutine\run()은 코루틴 컨텍스트를 열고, go()는 새 코루틴을 시작합니다. 각 코루틴에는 파이버처럼 자체 스택이 있지만, Swoole은 입출력 지점에서 코루틴을 자동으로 스케줄링합니다.

<?php
use Swoole\Coroutine as Co;

Co\run(function () {
    go(function () { Co::sleep(1); echo "A done\n"; });
    go(function () { Co::sleep(1); echo "B done\n"; });
    // Both sleep concurrently -> total ~1s, not 2s
});

후크가 적용된 차단 함수

코루틴을 편리하게 만드는 핵심은 런타임 후크입니다. Swoole은 차단 호출인 PDO, Redis, curl, sleep, file_get_contents를 코루틴을 인식하는 비차단 버전으로 투명하게 다시 작성합니다. 일반적인 $pdo->query()는 작업자를 멈추는 대신 코루틴을 양보합니다.

<?php
use Swoole\Runtime;
use Swoole\Coroutine as Co;

Runtime::enableCoroutine(SWOOLE_HOOK_ALL); // hook PDO, curl, sleep, etc.

Co\run(function () {
    go(function () { sleep(1); echo "task 1\n"; }); // sleep() is now async!
    go(function () { sleep(1); echo "task 2\n"; });
}); // finishes in ~1s because sleep was hooked

WaitGroup을 사용한 동시 입출력

작업을 분산 실행하고 합류하려면 Swoole에서 Go의 sync.WaitGroup과 같은 WaitGroup을 제공합니다. 코루틴을 시작하고, 작업마다 add()를 호출하며, 완료 시 done()을 호출하고, 모든 작업이 동시에 완료될 때까지 wait()으로 기다립니다.

<?php
use Swoole\Coroutine as Co;
use Swoole\Coroutine\WaitGroup;

Co\run(function () {
    $wg = new WaitGroup();
    $results = [];
    foreach (['eu', 'us', 'asia'] as $region) {
        $wg->add();
        go(function () use ($wg, $region, &$results) {
            Co::sleep(0.5);              // simulated API call
            $results[$region] = "ok-$region";
            $wg->done();
        });
    }
    $wg->wait();                         // all three ran in parallel (~0.5s)
    var_dump($results);
});

통신을 위한 채널

코루틴은 채널을 통해 협력합니다. 채널은 타입이 지정되고 크기가 제한된 큐로, 가득 차면 생산자를 일시 중단하고 비어 있으면 소비자를 일시 중단합니다. 이는 CSP 방식의 메시징으로, 공유 상태 잠금을 완전히 피할 수 있습니다.

<?php
use Swoole\Coroutine as Co;
use Swoole\Coroutine\Channel;

Co\run(function () {
    $chan = new Channel(2);
    go(function () use ($chan) {
        foreach (range(1, 3) as $n) $chan->push($n);
        $chan->push(null); // sentinel
    });
    go(function () use ($chan) {
        while (($v = $chan->pop()) !== null) echo "got $v\n";
    });
});

연결 풀링

지속형 서버는 요청마다 데이터베이스 연결을 새로 열어서는 안 됩니다. Swoole은 코루틴에서 안전하게 사용할 수 있는 연결 풀을 제공하므로, 작업자는 수천 개의 동시 코루틴에서 고정된 연결 집합을 재사용하고 연결이 계속 생성되고 폐기되는 비용을 없앨 수 있습니다.

<?php
use Swoole\Database\PDOConfig;
use Swoole\Database\PDOPool;
use Swoole\Coroutine as Co;

Co\run(function () {
    $pool = new PDOPool((new PDOConfig())
        ->withHost('127.0.0.1')->withDbname('app')
        ->withUsername('u')->withPassword('p'), 8);

    go(function () use ($pool) {
        $pdo = $pool->get();           // borrow
        $row = $pdo->query('SELECT NOW()')->fetch();
        $pool->put($pdo);              // return to pool
        var_dump($row);
    });
});

상태 누수: 상주 메모리의 함정

프로세스가 여러 요청에 걸쳐 계속 실행되므로 전역 및 정적 상태가 유지됩니다. 무한히 커지는 정적 캐시, 요청별 데이터를 보유한 싱글턴, 또는 유출된 데이터베이스 트랜잭션은 이후 요청을 손상시키고 메모리를 고갈시킵니다. 규칙은 다음과 같습니다. 정적 변수나 싱글턴에 요청 범위 데이터를 저장하지 말고, 요청마다 컨텍스트를 초기화하며, 작업자가 주기적으로 재활용되도록 max_request를 설정하십시오.

<?php
$server = new Swoole\Http\Server('0.0.0.0', 9501);
$server->set([
    'worker_num'  => 8,
    'max_request' => 10000, // recycle a worker after N requests to bound leaks
]);
// Never store $request data in a static property between requests!

코루틴 로컬 컨텍스트

하나의 작업자가 여러 코루틴을 번갈아 실행하므로 요청별 데이터에 일반 정적 변수를 사용할 수 없습니다. Swoole은 각 코루틴에 컨텍스트(Coroutine::getContext())를 제공합니다. 이 객체의 수명은 코루틴과 같으므로 현재 사용자, 요청 ID 또는 트랜잭션 핸들을 저장하기에 적합합니다.

<?php
use Swoole\Coroutine as Co;

Co\run(function () {
    go(function () {
        $ctx = Co::getContext();
        $ctx['request_id'] = 'req-123';
        // ... deep call chain can read Co::getContext()['request_id']
        echo Co::getContext()['request_id'], PHP_EOL;
    });
});

프로세스 모델과 작업 프로세스

Swoole은 주 프로세스(이벤트 루프 스레드 관리), 관리자 프로세스(작업자 생성 및 모니터링), 작업자 프로세스(요청 처리기와 코루틴 실행), 그리고 선택적으로 $server->task()를 통해 오프로드되는 무겁거나 차단되거나 CPU를 많이 사용하는 작업을 위한 작업 처리 프로세스를 실행합니다. 이미지 처리나 오래 걸리는 CPU 작업에는 작업 처리 프로세스를 사용하여 요청 코루틴이 멈추지 않게 하십시오. 그러면 동시성과 병렬성을 모두 얻을 수 있습니다.

지연 실행과 타이머

Swoole에서는 Coroutine::defer()를 사용하여 현재 코루틴이 끝난 후 작업을 예약할 수 있습니다. 예외가 발생하더라도 풀에서 가져온 리소스를 확실하게 반환해야 할 때 유용합니다. 서버 전체에 적용되는 타이머(Timer::tick)는 외부 예약 작업 없이 작업자 내부에서 캐시 갱신과 같은 주기적인 작업을 실행합니다.

<?php
use Swoole\Coroutine as Co;

Co\run(function () {
    go(function () {
        $conn = 'borrowed-connection';
        Co::defer(function () use ($conn) {
            echo "released $conn\n"; // runs when coroutine exits, even on error
        });
        echo "using $conn\n";
    });
});

빠른 확인

Swoole 서버에서 정적 속성에 요청 데이터를 저장하지 않아야 하는 이유는 무엇입니까?

복습

Swoole은 PHP를 상주형 코루틴 기반 서버로 바꿉니다:

  • 애플리케이션은 한 번만 부팅되고, 작업자 풀이 내장된 코루틴(Co\run/go())으로 요청을 처리합니다.
  • 런타임 후크는 일반적인 PDO 호출, 컬 요청, 대기 호출을 비차단으로 만들며, WaitGroup과 채널은 동시 작업을 조정합니다.
  • 연결 풀은 코루틴 간에 데이터베이스 연결을 재사용합니다.
  • 메모리가 지속되므로 상태 누수를 피해야 합니다. 요청별 데이터에는 Coroutine::getContext()를 사용하고, max_request를 설정하며, 무거운 작업은 작업 처리 프로세스로 오프로드하십시오.
무료로 시작

AI 튜터와 함께 PHP을(를) 배우세요 — 무료

브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.

코스
49
레슨
195

자주 묻는 질문

“Swoole을 활용한 고성능 서버” 강의는 무료인가요?

네 — “Swoole을 활용한 고성능 서버” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 PHP Academy 강의 전체를 잠금 해제할 수 있습니다. PHP Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“Swoole을 활용한 고성능 서버”에서 뭘 배우나요?

코루틴 기반 Swoole로 수천 개의 연결을 처리합니다. 브라우저에서 직접 실행하는 실습 코드로 PHP Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

PHP Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 PHP Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.

“Swoole을 활용한 고성능 서버” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 PHP Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 PHP Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. PHP 동시성 모델
  2. PHP 8.1 파이버
  3. ReactPHP의 이벤트 루프
  4. Swoole을 활용한 고성능 서버
← PHP Academy(으)로 돌아가기