Прикладная демонология, практические рецепты

Завершение рабочих процессов Apache (1.3)

У веб-сервера Apache имеется полезная директива MaxSpareServers. Она позволяет указать максимально допустимое количество бездействующих рабочих процессов.
Таким образом, после пиковой нагрузки, master-процесс начинает завершать избыточные рабочие процессы пока количество простаивающих не упадет до заданной директивой величины.
Причем делает он это раз в секунду, завершая по одному процессу за один проход цикла. К сожалению, в условиях довольно высокой нагрузки и "тяжелых" процессов Apache (например за счет использования интерпретаторов скриптовых языков), такое быстрое завершение зачастую является поспешным.
Предложенный ниже патч добавляет новую конфигурационную директиву (SpareReaperDelay), которая измеряется в секундах.
Дословный смысл ее таков: "только если в течении указанного времени сохраняется постоянное превышение MaxSpareServers, то можно завершить один рабочий процесс".
Патч вполне успешно использовался на ряде проектов компании "Rambler" в сочетании с mod_perl.
По поводу адаптации патча к Apache версий 2+ не предпринималось никаких усилий.

diff -ru ./src/include/http_conf_globals.h /home/lonerr/tmp/apache13/apache_1.3.41/src/include/http_conf_globals.h
--- ./src/include/http_conf_globals.h   2006-07-12 12:16:05.000000000 +0400
+++ /home/lonerr/tmp/apache13/apache_1.3.41/src/include/http_conf_globals.h 2008-03-06 14:34:45.000000000 +0300
@@ -45,6 +45,7 @@
 extern API_VAR_EXPORT int ap_daemons_to_start;
 extern API_VAR_EXPORT int ap_daemons_min_free;
 extern API_VAR_EXPORT int ap_daemons_max_free;
+extern API_VAR_EXPORT int ap_spare_reaper_delay;
 extern API_VAR_EXPORT int ap_daemons_limit;
 extern API_VAR_EXPORT int ap_suexec_enabled;
 extern API_VAR_EXPORT int ap_listenbacklog;
diff -ru ./src/main/http_core.c /home/lonerr/tmp/apache13/apache_1.3.41/src/main/http_core.c
--- ./src/main/http_core.c  2006-07-12 12:16:05.000000000 +0400
+++ /home/lonerr/tmp/apache13/apache_1.3.41/src/main/http_core.c    2008-03-06 14:34:53.000000000 +0300
@@ -2478,6 +2478,17 @@
     return NULL;
 }

+static const char *set_spare_reaper_delay(cmd_parms *cmd, void *dummy, char *arg)
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    ap_spare_reaper_delay = atoi(arg);
+    return NULL;
+}
+
 static const char *set_server_limit (cmd_parms *cmd, void *dummy, char *arg)
 {
     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
@@ -3616,6 +3627,8 @@
   "Minimum number of idle children, to handle request spikes" },
 { "MaxSpareServers", set_max_free_servers, NULL, RSRC_CONF, TAKE1,
   "Maximum number of idle children" },
+{ "SpareReaperDelay", set_spare_reaper_delay, NULL, RSRC_CONF, TAKE1,
+  "Delay to kill spare servers" },
 { "MaxServers", set_max_free_servers, NULL, RSRC_CONF, TAKE1,
   "Deprecated equivalent to MaxSpareServers" },
 { "ServersSafetyLimit", set_server_limit, NULL, RSRC_CONF, TAKE1,
diff -ru ./src/main/http_main.c /home/lonerr/tmp/apache13/apache_1.3.41/src/main/http_main.c
--- ./src/main/http_main.c  2007-11-16 00:31:15.000000000 +0300
+++ /home/lonerr/tmp/apache13/apache_1.3.41/src/main/http_main.c    2008-03-06 14:34:53.000000000 +0300
@@ -214,6 +214,7 @@
 API_VAR_EXPORT int ap_daemons_to_start=0;
 API_VAR_EXPORT int ap_daemons_min_free=0;
 API_VAR_EXPORT int ap_daemons_max_free=0;
+API_VAR_EXPORT int ap_spare_reaper_delay=0;
 API_VAR_EXPORT int ap_daemons_limit=0;
 API_VAR_EXPORT time_t ap_restart_time=0;
 API_VAR_EXPORT int ap_suexec_enabled = 0;
@@ -5117,7 +5118,7 @@
 #define SIG_TIMEOUT_KILL SIGALRM
 #endif

-static void perform_idle_server_maintenance(void)
+static void perform_idle_server_maintenance(int *rloop)
 {
     int i;
     int to_kill;
@@ -5209,11 +5210,20 @@
     */
         pid = ap_scoreboard_image->parent[to_kill].pid;
         if (in_pid_table(pid)) {
-            kill(pid, SIG_IDLE_KILL);
-            idle_spawn_rate = 1;
+            if (*rloop >= ap_spare_reaper_delay) {
+                ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
+                    "reaping spare child (pid: %d, delay: %d/%d)", pid, *rloop, ap_spare_reaper_delay);
+                kill(pid, SIG_IDLE_KILL);
+                idle_spawn_rate = 1;
+                *rloop = 0;
 #ifdef TPF
-            ap_update_child_status(to_kill, SERVER_DEAD, (request_rec *)NULL);
+                ap_update_child_status(to_kill, SERVER_DEAD, (request_rec *)NULL);
 #endif
+            } else {
+                ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, server_conf,
+                    "empty reaper cycle (delay: %d/%d)", *rloop, ap_spare_reaper_delay);
+                ++*rloop;
+            }
         }
         else {
             ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf,
@@ -5221,6 +5231,7 @@
         }
     }
     else if (idle_count < ap_daemons_min_free) {
+   *rloop = 0;
    /* terminate the free list */
    if (free_length == 0) {
        /* only report this condition once */
@@ -5268,6 +5279,7 @@
    }
     }
     else {
+   *rloop = 0;
    idle_spawn_rate = 1;
     }
 }
@@ -5450,6 +5462,7 @@
             amutex->name, ap_default_mutex_method());
    restart_pending = shutdown_pending = 0;

+   int reaper_loop = 0;
    while (!restart_pending && !shutdown_pending) {
        int child_slot;
        ap_wait_t status;
@@ -5517,7 +5530,7 @@
        continue;
        }

-       perform_idle_server_maintenance();
+       perform_idle_server_maintenance(&reaper_loop);
    }

    if (shutdown_pending) {