개발 전 필독

Workerman으로 애플리케이션을 개발하기 위해서는 다음 내용을 이해해야 합니다.

1. Workerman 개발과 일반 PHP 개발의 차이점

HTTP 프로토콜 관련 변수 함수는 직접 사용할 수 없지만, Workerman 개발은 일반 PHP 개발과 크게 다르지 않습니다.

1. 애플리케이션 계층 프로토콜의 차이

  • 일반 PHP 개발은 보통 HTTP 애플리케이션 계층 프로토콜을 기반으로 하며, WebServer가 개발자가 프로토콜을 분석하는 것을 도와줍니다.
  • Workerman은 다양한 프로토콜을 지원하며, 현재 내장되어 있는 HTTP, WebSocket 등의 프로토콜이 있습니다. Workerman은 개발자가 더 간단한 커스텀 프로토콜 통신을 사용할 것을 추천합니다.

2. 요청 주기 차이

  • PHP는 웹 애플리케이션에서 한 번의 요청 후 모든 변수와 리소스를 해제합니다.
  • Workerman으로 개발한 애플리케이션은 처음 로드되면 메모리에 상주하게 되어, 클래스의 정의, 전역 객체 및 클래스의 정적 멤버가 해제되지 않고 이후 재사용할 수 있습니다.

3. 클래스와 상수의 중복 정의 피하기

  • Workerman은 컴파일된 PHP 파일을 캐시하기 때문에 동일한 클래스 또는 상수 정의 파일을 여러 번 require/include하는 것을 피해야 합니다. 파일 로드를 위해 require_once/include_once를 사용하는 것이 좋습니다.

4. 싱글턴 패턴의 연결 리소스 해제 주의

  • Workerman은 매 요청 후 전역 객체 및 클래스의 정적 멤버를 해제하지 않기 때문에, 데이터베이스 등의 싱글턴 패턴에서는 데이터베이스 인스턴스(내부에 데이터베이스 소켓 연결이 포함되어 있음)를 데이터베이스 정적 멤버에 저장하는 경우가 많습니다. 이로 인해 Workerman은 프로세스 생애 주기 내내 이 데이터베이스 소켓 연결을 재사용합니다. 데이터베이스 서버는 특정 연결이 일정 시간 동안 비활성 상태일 경우 소켓 연결을 닫을 수 있으므로, 이후 이 데이터베이스 인스턴스를 사용할 때 오류가 발생할 수 있습니다(오류 메시지는 mysql gone away와 유사). Workerman은 데이터베이스 클래스를 제공하며, 자동 재연결 기능이 있어 개발자가 직접 사용할 수 있습니다.

5. exit, die 문을 사용하지 않기

  • Workerman은 PHP 명령줄 모드에서 실행됩니다. exit, die와 같은 종료 문을 호출하면 현재 프로세스가 종료됩니다. 자식 프로세스가 종료된 후 즉시 동일한 자식 프로세스를 재생성하여 서비스를 계속하지만, 여전히 비즈니스에 영향을 미칠 수 있습니다.

6. 코드를 수정한 후에는 서비스를 재시작해야 합니다

Workerman은 상주 메모리에서 실행되므로, php 클래스 및 함수의 정의가Load된 후 메모리에 상주하게 되어 디스크에서 다시 읽지 않습니다. 따라서 비즈니스 코드를 수정할 때마다 재시작해야 효과가 발생합니다.

2. 알아야 할 기본 개념

1. TCP 전송 계층 프로토콜

TCP는 연결 지향적인, 신뢰할 수 있는, IP 기반의 전송 계층 프로토콜입니다. TCP 전송 계층 프로토콜의 중요한 특징은 TCP가 데이터 흐름 기반이라는 것입니다. 클라이언트의 요청은 지속적으로 서버에 전송되며, 서버가 받는 데이터는 하나의 완전한 요청이 아닐 수도 있고 여러 요청이 연결되어 있을 수도 있습니다. 따라서 우리는 이러한 지속적인 데이터 흐름에서 각 요청의 경계를 구분해야 합니다. 애플리케이션 계층 프로토콜은 이러한 요청 경계를 정의하는 규칙 세트를 제공하여 요청 데이터의 혼란을 피합니다.

2. 애플리케이션 계층 프로토콜

애플리케이션 계층 프로토콜(application layer protocol)은 서로 다른 엔드 시스템(클라이언트, 서버)에서 실행되는 애플리케이션 프로세스가 어떻게 메시지를 상호 전송하는지를 정의합니다. 예를 들어 HTTP, WebSocket은 애플리케이션 계층 프로토콜에 해당합니다. 예를 들어, 간단한 애플리케이션 계층 프로토콜은 다음과 같이 될 수 있습니다. {"module":"user","action":"getInfo","uid":456}\n" 이 프로토콜은 "\n"(여기서 "\n"은 줄 바꿈을 의미합니다)로 요청의 끝을 표시하고, 메시지 본문은 문자열입니다.

3. 짧은 연결

짧은 연결은 통신 쌍방이 데이터 상호작용할 때마다 연결을 설정하고, 데이터 전송이 완료되면 이 연결을 끊는 것을 말합니다. 즉, 매 연결은 하나의 비즈니스 전송만을 완료합니다. 웹 사이트의 HTTP 서비스는 일반적으로 짧은 연결을 사용합니다.

짧은 연결 애플리케이션 개발은 기본 개발 프로세스 장을 참고하십시오.

4. 긴 연결

긴 연결은 하나의 연결에서 여러 데이터 패킷을 연속적으로 전송할 수 있는 것을 말합니다.

주의: 긴 연결 애플리케이션은 하트비트를 추가해야 하며, 그렇지 않으면 연결이 오랜 시간 비활성 상태로 인해 라우터 노드 방화벽에 의해 끊어질 수 있습니다.

긴 연결은 빈번한 작업 및 피어 투 피어 통신 상황에 주로 사용됩니다. 각 TCP 연결은 3단계 핸드쉐이크가 필요하며, 이 과정에는 시간이 필요합니다. 각 작업이 연결 후 진행되면 속도가 크게 저하되므로, 긴 연결은 각 작업 후 연결을 끊지 않고 다음 처리를 위해 데이터 패킷을 직접 전송하는 것이 가능합니다. 예를 들어, 데이터베이스 연결은 긴 연결을 사용하며, 짧은 연결을 빈번하게 사용하면 소켓 오류가 발생할 수 있으며, 빈번한 소켓 생성은 자원 낭비를 초래합니다.

클라이언트로 데이터를 적극적으로 푸시해야 하는 경우, 예를 들어 채팅, 실시간 게임, 모바일 푸시 등 애플리케이션은 긴 연결이 필요합니다.
긴 연결 애플리케이션 개발은 Gateway/Worker 개발 프로세스 참고하십시오.

5. 부드러운 재시작

일반적인 재시작 과정은 모든 프로세스를 완전히 중지한 후 새로운 서비스 프로세스를 시작하는 것입니다. 이 과정에서는 일시적으로 어떠한 프로세스도 외부 서비스를 제공하지 않게 되므로, 이는 고부하 환경에서 요청 실패를 초래할 수 있습니다.

부드러운 재시작은 모든 프로세스를 일시 중지하는 것이 아니라, 프로세스를 하나씩 중지한 후, 각 프로세스를 중지할 때마다 즉시 새로운 프로세스를 생성하여 모든 이전 프로세스가 교체될 때까지 진행됩니다.

Workerman에서 부드러운 재시작을 수행하려면 php your_file.php reload 명령을 사용하면 됩니다. 이 명령은 서비스 품질에 영향을 주지 않고 애플리케이션을 업데이트할 수 있습니다.

주의: on{...} 콜백에서 로드된 파일만 부드러운 재시작 후 자동으로 업데이트되며, 시작 스크립트에서 직접 로드된 파일이나 하드 코딩된 코드에서 실행된 reload는 자동으로 업데이트되지 않습니다.

3. 주 프로세스와 자식 프로세스 구별하기

코드가 주 프로세스에서 실행되는지 자식 프로세스에서 실행되는지를 주의할 필요가 있습니다. 일반적으로 Worker::runAll(); 호출 전의 코드는 주 프로세스에서 실행되며, onXXX 콜백에서 실행되는 코드는 모두 자식 프로세스에 해당합니다. Worker::runAll(); 이후의 코드는 절대 실행되지 않습니다.

예를 들어 아래 코드:

use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';

// 주 프로세스에서 실행
$tcp_worker = new Worker("tcp://0.0.0.0:2347");
// 할당 과정은 주 프로세스에서 실행
$tcp_worker->onMessage = function(TcpConnection $connection, $data)
{
    // 이 부분은 자식 프로세스에서 실행
    $connection->send('hello ' . $data);
};

Worker::runAll();

주의: 주 프로세스에서 데이터베이스, memcache, redis 등의 연결 리소스를 초기화하지 마십시오. 주 프로세스에서 초기화된 연결은 자식 프로세스에 자동으로 상속될 수 있으며(특히 싱글턴을 사용할 경우) 모든 프로세스가 동일한 연결을 보유하게 되어, 서버가 이 연결을 통해 반환된 데이터는 여러 프로세스에서 모두 읽을 수 있어 데이터가 혼란스러울 수 있습니다. 마찬가지로 어떤 프로세스가 연결을 종료하면(예: 데몬 모드에서 주 프로세스가 종료되어 연결이 종료됨), 모든 자식 프로세스의 연결이 함께 종료되어 예측할 수 없는 오류가 발생할 수 있습니다(예: mysql gone away 오류).

onWorkerStart 내에서 연결 리소스를 초기화하는 것을 권장합니다.