Hyperf HTTP 서버 부팅 과정 분석

Hyperf 프로젝트를 생성한 후 터미널에서 php bin/hyperf.php start 명령을 실행하면, 잠시 후 워커 프로세스가 시작되고 HTTP 서버가 9501 포트에서 대기 중이라는 로그가 출력된다.

[INFO] Worker#3 started.  
[INFO] Worker#1 started.  
[INFO] Worker#2 started.  
[INFO] Worker#0 started.  
[INFO] HTTP Server listening at 0.0.0.0:9501

브라우저에서 http://127.0.0.1:9501에 접속하면 Hello Hyperf 메시지가 표시된다. 이 글에서는 이 HTTP 서버가 어떻게 초기화되고 실행되는지 내부 메커니을 살펴본다.

bin/hyperf.php: 프레임워크 진입점

실행 명령에서 PHP 바이너리 외에 주목할 부분은 두 가지다.

  • bin/hyperf.php: 프레임워크 진입점
  • start: 실행할 명령어 인자

진입점 파일의 실행 흐름은 네 단계로 구분할 수 있다.

1단계: 런타임 환경 구성

PHP 내장 함수를 호출하여 메모리 제한, 오류 수준, 시간대 등 기본 설정을 초기화한다. 이 단계에서 정의되는 두 가지 상수가 중요하다.

  • BASE_PATH: 프로젝트 루트 디렉토리의 절대 경로. Hyperf 내부에서 파일 경로 탐색의 기준점으로 사용된다.
  • SWOOLE_HOOK_FLAGS: Swoole의 코루틴 훅 범위를 지정. SWOOLE_HOOK_ALL은 모든 블로킹 I/O를 비동기 코루틴으로 전환하는 "원 클릭 코루틴화"를 활성화한다.

2단계: 클래스 로더 초기화

Hyperf의 어노테이션 기반 기능(의존성 주입, AOP, 라우팅 등)이 동작하려면 클래스 로더의 사전 준비가 필요하다.

  • 어노테이션 정보 수집 및 컬렉터 초기화
  • AOP와 @Inject를 위한 프록시 클래스 생성
  • 런타임 캐시 생성으로 부팅 속도 향상

3단계: DI 컨테이너 구축

config/autoload/dependencies.php와 각 컴포넌트의 ConfigProvider에 정의된 의존성 관계를 읽어 컨테이너에 등록한다.

4단계: CLI 애플리케이션 준비

Hyperf는 사실상 콘솔 애플리케이션이다. start, migrate, gen 등 내장 명령과 사용자 정의 명령을 애플리케이션에 등록한다.

HTTP 서버 초기화 및 실행

초기화가 완료되면 CLI 애플리케이션이 인자를 파싱하여 start에 해당하는 StartServer 명령(hyperf/server 컴포넌트)을 실행한다.

환경 검증

Swoole 확장 설치 여부와 함수 단축명(short name) 비활성화 상태를 확인한다. 단축명이 활성화되면 경고 후 종료한다.

설정 파일 로드

config/autoload/server.php에서 서버 설정을 읽는다. 여기서 server.typeserver.servers.type의 차이를 이해하는 것이 중요하다.

Swoole은 두 가지 서버 스타일을 제공한다.

특성비동기 스타일코루틴 스타일
런타임 조작시작 후 변경 불가동적 생성/소멸 가능
연결 처리순서 보장 불가별도 코루틴에서 순차 처리

Hyperf는 확장성을 위해 ServerInterface를 추상화했다.

interface ServerInterface
{    
    const SERVER_HTTP = 1;    
    const SERVER_WEBSOCKET = 2;    
    const SERVER_BASE = 3;    
    
    public function __construct(
        ContainerInterface $container, 
        LoggerInterface $logger, 
        EventDispatcherInterface $dispatcher
    );    
    
    public function init(ServerConfig $config): ServerInterface;    
    public function start(): void;
}

구현체는 다음과 같다.

  • Hyperf\Server\Server: Swoole 비동기 스타일
  • Hyperf\Server\CoroutineServer: Swoole 코루틴 스타일
  • Hyperf\Server\SwowServer: Swow 코루틴 스타일

server.type으로 사용할 구현체를 지정할 수 있고, ServerInterface를 구현하여 커스텀 서버도 추가 가능하다.

서버 인스턴스 생성

ServerFactory::configure에서 설정 배열을 ServerConfig 객체로 변환한다.

class ServerConfig implements Arrayable
{
    public function __construct(protected array $config = [])
    {
        $servers = [];
        foreach ($config['servers'] as $item) {
            $servers[] = Port::build($item);
        }

        $this->setType($config['type'] ?? Server::class)
            ->setMode($config['mode'] ?? 0)
            ->setServers($servers)
            ->setProcesses($config['processes'] ?? [])
            ->setSettings($config['settings'] ?? [])
            ->setCallbacks($config['callbacks'] ?? []);
    }
}

타입이 지정되지 않으면 기본으로 Hyperf\Server\Server(비동기 스타일)이 사용된다.

다중 서비스 지원 메커니즘

하나의 서버 구현체로 여러 서비스(HTTP, WebSocket, TCP)를 동시에 실행할 수 있다. Swoole의 비동기 서버는 addListener로 다중 포트 리스닝을 지원하기 때문이다.

Hyperf\Server\Server::init의 동작을 살펴보면:

  1. ServerFactory::sortServers로 WebSocket → HTTP → TCP 순서로 서비스 정렬
  2. 첫 번째 서비스: makeServer로 Swoole 서버 객체 생성 → 이벤트 콜백 등록 → 설정 적용
  3. 이후 서비스: addListener로 서브 서버 추가 → 이벤트 콜백 등록 → 설정 적용

makeServer의 분기 로직:

switch ($type) {  
    case ServerInterface::SERVER_HTTP:  
        return new Swoole\Http\Server($host, $port, $mode, $sockType);  
    case ServerInterface::SERVER_WEBSOCKET:  
        return new Swoole\WebSocket\Server($host, $port, $mode, $sockType);  
    case ServerInterface::SERVER_BASE:  
        return new Swoole\Server($host, $port, $mode, $sockType);  
}

이벤트 콜백 등록

Hyperf는 세 가지 이벤트 설정 레벨을 지원한다.

  • 글로벌: server.callbacks
  • 서비스별: server.servers.callbacks
  • 기본값: Server::defaultCallbacks()

우선순위: 서비스별 > 글로벌 > 기본값

$callbacks = array_replace(
    $this->defaultCallbacks(), 
    $config->getCallbacks(), 
    $callbacks
);

foreach ($callbacks as $event => $callback) {
    if (!Event::isSwooleEvent($event)) {
        continue;
    }
    // ...
    $server->on($event, $callback);
}

서버 기동

실행 전 코루틴 범위를 설정한다.

Coroutine::set(['hook_flags' => swoole_hook_flags()]);

ServerFactory::startServer::start → Swoole 서버 기동 순으로 호출된다.

Swoole 서버가 시작되면 Event::ON_WORKER_START 이벤트가 발생하고, WorkerStartCallback::onWorkerStart가 실행된다. 이 콜백에서 워커 시작 로그를 출력하고 AfterWorkerStart 이벤트를 발행한다. 해당 이벤트의 리스너인 AfterWorkerStartListener에서 HTTP 서버 리스닝 로그를 출력한다.

핵심 설계 인사이트

HTTP 서버의 부팅 과정을 이해하면 WebSocket, TCP 등 다른 서비스 타입의 동작 원리도 파악할 수 있다. 모든 서비스는 동일한 추상화 레벨에서 관리되며, 설정 우선순위와 다중 포트 바인딩 메커니즘을 공유한다.

문제 발생 시 부팅 단계를 순차적으로 점검하면 원인을 빠르게 특정할 수 있다: 환경 검증 → 설정 로드 → 서버 초기화 → 이벤트 바인딩 → 코루틴 설정 → 서버 기동.

태그: Hyperf Swoole PHP coroutine Dependency Injection

7월 4일 20:06에 게시됨