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입니다. 하지만 중요한 의미를 지니고 있습니다.
- ServerLimit
child process 수를 제한합니다.
- ThreadLimit
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에 대해서 설명합니다.
- StartServer
HTTP Server 기동 시에 생성되는 process의 수 입니다. 하지만 child process의 수는 요청량에 따라 동적으로 변화하므로 크게 의미는 없다고 할 수 있습니다.
- MaxClients
동시에 처리될 수 있는 최대 요청입니다. MaxClients를 너무 낮게 설정할 경우 resource가 놀게 되고, 너무 높게 설정할 경우 resource가 감당하기 힘든 많은 요청이 몰려들게 됩니다. MaxClients 이상의 요청은 queue 에 쌓이게 되며, default queue 511로 ListenBackLog 에 의하여 조정 가능합니다.
- MinSpareThreads
default는 75 이며, child process 당 최소로 유지되는 idle thread 수지요. 만일 idle thread 가 충분하지 않다면 child process 는 MinSpareThreads 이상이 될 때까지 idle thread 를 생성합니다.
- MaxSpareThreads
default는 250이며, child process 당 최대 idle thread 입니다. 만일 너무 많은 idle thread가 있다면 child process는 MaxSpareThread 가 될 때까지 idle thread 를 정리합니다.
- ThreadsPerChild
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.
- MaxRequestsPerChild
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.