Print
카테고리: [ Apache HTTP Server ]
조회수: 11656

1. 개요

MPM (Multi Processing Module)에 대한 글입니다.


2. Prefork (mpm_prefork_module)

HTTP Server 1.3 시대에 사용하던 Prefork 방식은 각 child process가 하나의 요청 처리를 담당합니다. 그리고 당연히 OS 상에서는 worker 방식에 비해서는 더 많은 메모리를 사용하게 됩니다. 대신 메모리 공간이 독립적이어서 안정적인 면이 있습니다.

빌드 옵션은 --with-mpm=prefork 이며, --with-mpm 옵션을 쓰지 않으면 기본으로 prefork입니다.

MaxClients 수정을 위해서는 server/mpm/prefork/prefork.c 소스를 수정합니다.

#define DEFAULT_SERVER_LIMIT 256 ...... static int server_limit = DEFAULT_SERVER_LIMIT;

본 문서에서 Prefork 방식에 대해서는 특별히 더 언급하지 않습니다. 아래부터는 모두 Worker 방식에 해당되는 설명입니다.


3. Worker (mpm_worker_module)

Worker 방식은 각 child process 내 복수 개의 thread 당 요청 처리를 담당합니다. Prefork 방식에 비해 적은 메모리를 사용하는 반면 process 내 thread 간 메모리 공유를 하므로 resource 경합이 발생할 수 있습니다.

빌드 옵션은 --with-mpm=worker 입니다.

3-1. 설정

아래는 default httpd_mpm.conf 중 일부입니다.

# worker MPM
# StartServers: initial number of server processes to start
# MaxClients: maximum number of simultaneous client connections
# MinSpareThreads: minimum number of worker threads which are kept spare
# MaxSpareThreads: maximum number of worker threads which are kept spare
# ThreadsPerChild: constant number of worker threads in each server process
# MaxRequestsPerChild: maximum number of requests a server process serves

    StartServers          2
    MaxClients          150
    MinSpareThreads      25
    MaxSpareThreads      75
    ThreadsPerChild      25
    MaxRequestsPerChild   0

3-2. ServerLimit, ThreadLimit

먼저 아래는 HTTP Server 기본으로 제공되는, 즉 위의 httpd_mpm.conf에서는 볼 수 없는 directive입니다. 하지만 중요한 의미를 지니고 있습니다.

child process 수를 제한합니다. 

child process 당 thread 수를 제한합니다. 

ServerLimit과 ThreadLimit의 설정 값을 알고 싶었는데 적절한 자료가 없어 소스를 찾아보았습니다. 즉, server/mpm/worker.c 파일을 보시면 됩니다.

우선 ServerLimit입니다.

#define DEFAULT_SERVER_LIMIT 16
#define MAX_SERVER_LIMIT 20000

기본 16에 최대 20000까지 설정이 가능합니다. 설정이 가능하다는 말은 httpd_mpm.conf에서 ServerLimit directive로 설정이 가능하다는 것입니다. 만일 20000이 부족하면 소스 수정 후에 다시 빌드하면 되겠습니다만은, 그럴 일이 생길 일은 거의 없지 않을까요.

다음은 ThreadLimit입니다.

#define DEFAULT_THREAD_LIMIT 64
#define MAX_THREAD_LIMIT 20000

3-3. 주요 설정

그리고 이제야 위에서 보았던 directive에 대해서 설명합니다.

HTTP Server 기동 시에 생성되는 process의 수 입니다. 하지만 child process의 수는 요청량에 따라 동적으로 변화하므로 크게 의미는 없다고 할 수 있습니다.

동시에 처리될 수 있는 최대 요청입니다. MaxClients를 너무 낮게 설정할 경우 resource가 놀게 되고, 너무 높게 설정할 경우 resource가 감당하기 힘든 많은 요청이 몰려들게 됩니다. MaxClients 이상의 요청은 queue 에 쌓이게 되며, default queue 511로 ListenBackLog 에 의하여 조정 가능합니다.

default는 75 이며, child process 당 최소로 유지되는 idle thread 수지요. 만일 idle thread 가 충분하지 않다면 child process 는 MinSpareThreads 이상이 될 때까지 idle thread 를 생성합니다.

default는 250이며, child process 당 최대 idle thread 입니다. 만일 너무 많은 idle thread가 있다면 child process는 MaxSpareThread 가 될 때까지 idle thread 를 정리합니다.

default 는 25이며, child process 당 thread 수 입니다. server/mpm/mpm_default.h에서 default 확인이 가능합니다.

#define DEFAULT_THREADS_PER_CHILD 25

max 는 ThreadLimit 을 따라갑니다. 위에서 ThreadLimit 의 default 가 64 라고 이야기 한 바 있구요, 따라서 만일 64 를 초과하는 값을 부여할 경우 다음과 같은 오류가 납니다. (512를 준 경우입니다)

$ ./start.sh
WARNING: ThreadsPerChild of 512 exceeds ThreadLimit value of 64
threads, lowering ThreadsPerChild to 64. To increase, please see the
 ThreadLimit directive.

0이면 무제한입니다. 간혹 장기간 운영시 메모리 누수에 따른 이슈가 발생하는 경우가 있습니다. 이 때는 MaxRequestsPerChild 설정을 통해 각 프로세스 처리 횟수를 제한하여 초기화되도록 하는 것이 좋습니다.

3-4. MPM 확인

HTTP Server 가 어떤 방식으로 동작하는지 확인하려면 httpd -l 하면 됩니다.

$ ./httpd -l
Compiled in modules:
  core.c
  worker.c
  http_core.c
  mod_so.c 

4. 세부 설정

4-1. 기본

사실 제가 이야기하고 싶은 것은 지금부터 입니다. 다시 설정을 한번 봅니다.

<IfModule mpm_worker_module>
    StartServers          2
    MaxClients          150
    MinSpareThreads      25
    MaxSpareThreads      75
    ThreadsPerChild      25
    MaxRequestsPerChild   0
</IfModule>

명시적으로 표현되지 않은 ServerLimit는 default 16 이고, ThreadsPerChild이 25 이므로 최대 16*25, 총 400개의 동시 요청 처리가 가능하긴 합니다. 하지만 MaxClients 가 150 으로 설정되어 있으므로 실제적으로 최대로 기동되는 process의 수는 150/25 인 6개가 됩니다. 결론적으로 최초에 2개의 process 가 기동되어 50개의 요청처리가 가능한 상태가 되고 (50 threads) 이후 1개의 process가 증가하면서 25개의 요청처리가 더 가능해지고  (+25 threads) 최대 150개의 요청처리가 가능합니다. (150 threads)

4-2. MaxClients 증가

<IfModule mpm_worker_module>
    StartServers          2
    MaxClients         1000
    MinSpareThreads      25
    MaxSpareThreads      75
    ThreadsPerChild      25
    MaxRequestsPerChild   0
</IfModule>

이번에는 MaxClients 가 1000 으로 증가하였습니다. 결론을 얘기하자면 1000개의 동시 요청처리는 불가능합니다. 앞서 설명한대로 ServerLimit 16 에 ThreadsPerChild 25 이기 때문에 최대 400개의 동시 요청까지만 처리 가능하기 때문입니다. 만일 의도한대로 1000개의 동시 요청을 처리하게 하려면 ThreadsPerChild 를 63 이상으로 설정하거나 (16*63 = 1008), ServerLimit 을 40 이상으로 설정하던지 (40*25 = 1000), 두 값을 모두 조정하던지 해야 합니다.

4-3. ThreadsPerChild & ThreadLimit

ThreadsPerChild 는 ThreadLimit 보다 같거나 작아야 합니다. ThreadLimit 은 그냥 두고 (default 64), ThreadsPerChild 를 256 으로 올리면 다음과 같은 오류가 납니다.

WARNING: ThreadsPerChild of 256 exceeds ThreadLimit value of 64
threads, lowering ThreadsPerChild to 64. To increase, please see the
 ThreadLimit directive.

4-4. ThreadLimit 주의사항

ThreadLimit 설정시에도 주의할 점이 있습니다.

<IfModule mpm_worker_module>
    StartServers          4
    ServerLimit          32
    MaxClients         2048
    MinSpareThreads      64
    MaxSpareThreads     128
    ThreadLimit         128
    ThreadsPerChild     128
    MaxRequestsPerChild   0
</IfModule>

이론상 이렇게 설정하면 ThreadLimit이 128로 설정되어야 하겠지요.

WARNING: ThreadsPerChild of 128 exceeds ThreadLimit value of 64
threads, lowering ThreadsPerChild to 64. To increase, please see the
 ThreadLimit directive.

그런데 여전히 ThreadLimit이 64라고 합니다.

해결을 위하여 ThreadLimit의 순서를 바꾸어봅니다.

<IfModule mpm_worker_module>
    StartServers          4
    ServerLimit          32
    ThreadLimit         128
    MaxClients         2048
    MinSpareThreads      64
    MaxSpareThreads     128
    ThreadsPerChild     128
    MaxRequestsPerChild   0
</IfModule>

이번에는 오류가 발생하지 않습니다.

추가로 더 테스트 해본 바, ServerLimit 과 ThreadLimiit 은 반드시 MaxCLients 보다 앞서 기술되어야 합니다. ServerLimit * ThreadLimit >= MaxClients 어야 하기 때문일 것 같네요.

4-5. ServerLimit & MaxClients

또한 ServerLimit 을 그냥 두고 (default 16) MaxClients 를 올려서 16 개 이상의 Server 가 기동되어야 하는 경우 다음과 같은 오류가 납니다.

WARNING: MaxClients of 2048 would require 32 servers,
 and would exceed the ServerLimit value of 16.
 Automatically lowering MaxClients to 1024.  To increase,
 please see the ServerLimit directive.