본문

PHP Background task / Multitasking 구현하기

Mail함수등과 같은, 부하를 많이 주는 작업을 PHP에서 하게되면 클라이언트는 연결이 종료될때까지 마냥 기다리고 있어야 한다. 마찬가지로 만약 이 연결이 너무나 오래걸린다 싶으면 timeout이 걸려버려서 당황스럽게 만든다. 그렇다고 해서 set_time_limit만을 사용하면 그동안에 다른작업을 못하게 되므로 역시 문제가 된다. 물론 ajax를 통한 비동기적 통신이 이를 보완해줄 수 있겠지만 그래도 연결이 지속되기 때문에 시스템 효율상좋지 않을것으로 보인다.

제목에서 왜 백그라운드 작업과 멀티태스킹를 구분해 놓았냐면 백그라운드 작업은 실제 작업이 오래걸리는 알고리즘 상에 직접 적용하는 것을 의미하는것인 반면 멀티태스킹 부분은 오래걸리는 알고리즘을 외부에서 호출하는(->동시 작업에 있어서의 유용성을 얻기 위해) 부분에 대해 서술한 부분이기 떄문이다, 

1. 백그라운드 작업 구현하기(알고리즘 상에서)
이를 위해서 구글링을 한 결과 다음과 같은 소스를 얻을 수 있었다. 원리라면 타겟 알고리즘이 실행되기 전에 임의로 헤더를 작성한 다음에 그냥 뿌려버려서 클라이언트에게 이미 컨넥션이 끝났다는것을 알려주어 클라이언트와의 연결을 끊지만, 사실 그 뒤에 자신 나름대로 일을 묵묵히 하는것이다. 단점이라면 백그라운드에서 작업이 되므로 실제 알고리즘이 반환하는 값을 알 수 없으며, 게다가 알고리즘이 잘못되어 무한루프에 걸린경우 (+타임 리밋을 해제한경우)이에대해 컨트롤이 어렵다는 것이다. 이에 대한 보완방법은 이 글의 하단부에 놓을 것이다.

아무튼 이 소스를 타겟파일의 최상위층에 require()하여 사용하면 부하가 큰 작업에 대하여 쉬운 처리가 가능하다. 특이 함수를 사용하는것이 아니기 때문에 이자체와 PHP4, 5 에서 사용가능할것이다. 그리고 작업 환경에 따라서 상단에 set_time_limit(0);를 사용해 주는것도 고려할 만 하겠다.

while(ob_get_level()) ob_end_clean();
header('Connection: close');
ignore_user_abort();
ob_start();
echo('Connection Closed');
$size = ob_get_length();
header("Content-Length: $size");
ob_end_flush();
flush();

2. 멀티태스킹 구현하기(연결 상에서)
curl은 http, ftp, telnet등과 같은 프로토콜을 더욱 쉽게 사용하기 위해 사용되는 일종의 라이브러리이며, php에 기본적으로 딸려오는 것이 아니라 따로 설치해 주어야 한다. 함수를 오버라이딩 하느라 일부러 함수 작명을 이렇게 한건줄 알았는데 함수 리스트를 살펴보니 그건 또 아니었다...;; 아무튼 원리는 http://www.heavymetalcasting.com/megapingofdeath.php 라는 아웃풋이 한참 후에 나오는 페이지가 있다면(-_-;;) 페이지 요청을 보내서 서버측에서 작업을 시작하게 하고 그 즉시 연결을 끊어버리는 간단한 구조이다. 역시 PHP 4, 5에서 동작 가능하다.
 
function curl_post_async($url, $params)
{
    foreach ($params as $key => &$val) {
      if (is_array($val)) $val = implode(',', $val);
        $post_params[] = $key.'='.urlencode($val);
    }
    $post_string = implode('&', $post_params);
 
    $parts=parse_url($url);
 
    $fp = fsockopen($parts['host'],
        isset($parts['port'])?$parts['port']:80,
        $errno, $errstr, 30);
 
    $out = "POST ".$parts['path']." HTTP/1.1\r\n";
    $out.= "Host: ".$parts['host']."\r\n";
    $out.= "Content-Type: application/x-www-form-urlencoded\r\n";
    $out.= "Content-Length: ".strlen($post_string)."\r\n";
    $out.= "Connection: Close\r\n\r\n";
    if (isset($post_string)) $out.= $post_string;
 
    fwrite($fp, $out);
    fclose($fp);
}

보완 가능한 부분 :
위 두 방법의 단점이라 하면 결과값을 받기 전에 클라이언트와 서버에 있어서의 연결이 종료되기 때문에 실제 알고리즘이 반환하는 값에대해서는 트래킹이 불가능하다는 것이다. 하지만 이 점은 약간의 보완을 통해 해결할 수 있는데, 바로 프로그램을 실행하기 전에 DB와 ID값을 사용하여, 알고리즘 종료시 DB에 결과값을 저장하고, 클라이언트 측에서 지속적으로 ID값을 사용하여 결과값을 확인하게 하는것이다.

그리고 위에 있는 멀티태스킹을 구현한 함수에서의 문제라면 fsockopen함수는 lookup과정과 연결확립이 완료 된 후에 뒤에 있는 작업들을 처리할 수 있다는 점이다. 그렇기 때문에 PHP5에서 제공하는 stream_socket_client라는 함수를 사용하여 더욱 더 비동기적인 작업의 처리가 가능할 것이다.


소스 출처 : http://stackoverflow.com/questions/124462/asynchronous-php-calls

댓글

Holic Spirit :: Tistory Edition

design by tokiidesu. powerd by kakao.