2011-04-26 11 views
22

Por lo tanto, enviar el USR2 a Unicorn es increíble: inicia un nuevo maestro con una nueva copia de su código y automáticamente detecta cualquier cambio. Dulce. Mi pregunta es: ¿cómo detengo al viejo maestro? La forma aparentemente aceptado se encuentra en una before_fork:Reiniciar Unicornio con un USR2 - salir del viejo maestro

before_fork do |server,worker| 
    old_pid = '/var/www/current/tmp/pids/unicorn.pid.oldbin' 
    if File.exists?(old_pid) && server.pid != old_pid 
    begin 
     Process.kill("QUIT", File.read(old_pid).to_i) 
    rescue Errno::ENOENT, Errno::ESRCH 
     # someone else did our job for us 
    end 
    end 
end 

El problema con esto es que tan pronto como el nuevo maestro (y nuevos trabajadores) se generan, matan el viejo maestro. Por lo tanto, cualquier solicitud al sitio se queda allí esperando que comience el nuevo trabajador, generalmente durante varios segundos mientras se carga toda la pila de Rails.

Si elimino mi before_fork todo funciona como esperaría (desde el punto de vista del cliente): puedo recargar mi navegador todo el día y cada solicitud se completa rápidamente, no hay indicación de cuándo el nuevo maestro se hace cargo (otro que ver mis cambios de código aparecer ahora). Pero, el viejo maestro ahora se cuelga hasta que manualmente le envíe un QUIT.

Hasta donde sé, no hay devolución de llamada una vez que un trabajador termina de cargar y listo para atender a los clientes. Esa es realmente la devolución de llamada que estoy buscando. Siempre podría crear un inicializador en Rails que busque un viejo maestro y lo mate, pero eso me daña el corazón solo de pensarlo.

¡Debe haber una manera!

+0

Estoy bastante seguro de que after_fork es llamado por los trabajadores una vez que hayan terminado de cargarse, probablemente puedas poner el código ahí .. –

Respuesta

16

He resuelto parcialmente esto: el comportamiento que estoy viendo es causado por no usar preload_app true. Si tiene este conjunto, el maestro carga la aplicación completa y los trabajadores son muy rápidos para engendrar. Entonces, si el primer trabajador mata al viejo maestro en este punto, está bien porque dicho trabajador puede comenzar a atender las solicitudes de inmediato.

Si no puede usar preload_app true, la mejor opción es mover ese comportamiento old-pid-quit al inicializador de Rails para que el primer trabajador que lo muestra pueda matar el antiguo maestro una vez que Rails se haya puesto en marcha y está listo para atender las solicitudes.

+0

¿Cuántos procesos de trabajo está ejecutando? – Ivan

8

Parece que enviar una señal HUP al maestro Unicornio es una mejor alternativa si preload_app es false.

De http://unicorn.bogomips.org/SIGNALS.html:

HUP - vuelve a cargar el archivo de configuración y reinicie con gracia a todos los trabajadores. Si la directiva "preload_app" es falsa (el valor predeterminado es ), los trabajadores también seleccionarán con cualquier cambio de código de aplicación cuando se reinicie .

+4

Acutally, si preload_app es verdadero, debe usar USR2 + QUIT 'Si "preload_app" es verdadero, entonces los cambios en el código de la aplicación no tendrán efecto; USR2 + QUIT se debe usar para cargar código más nuevo en este caso. ' – astjohn

+0

Whoops. Quise decir "si preload_app es falso". –

+1

Si esto era cierto antes, ya no es: HUP vuelve a cargar la aplicación incluso si preload_app es verdadero. – Kevin

3

Esto es lo que tengo en mi bloque before_fork:

old_pid = "#{server.config[:pid]}.oldbin" 
    if old_pid != server.pid 
    begin 
     sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU 
     Process.kill(sig, File.read(old_pid).to_i) 
    rescue Errno::ENOENT, Errno::ESRCH 
    end 
    end 

Para reiniciar mi unicornio Tengo un script bash que tiene un método como este:

if sig USR2 && sig 0 && oldsig QUIT 
then 
    n=$TIMEOUT 
    while test -s $old_pid && test $n -ge 0 
    do 
     printf '.' && sleep 1 && n=$(($n - 1)) 
    done 
    echo 

    if test $n -lt 0 && test -s $old_pid 
    then 
     echo >&2 "$old_pid still exists after $TIMEOUT seconds" 
     exit 1 
    fi 
    exit 0 
fi 
echo >&2 "Couldn't upgrade, starting '$CMD' instead" 
$CMD 
;; 

La escritura del golpe envía una señal USR2, que bifurca al nuevo unicornio y crea el viejo pid. Luego envía al viejo unicornio la señal QUIT usando el viejo pid.

Este proceso funciona muy bien y fue tomado de Where Unicorns go to die: Watching unicorn workers with monit, que es un excelente recurso.

+0

USR2 no está creando 'oldbin' para mí, ¿cuál podría ser el motivo? – juanpastas