2012-04-03 9 views
8

¿Cuál es la mejor manera de insertar un nuevo código en un servidor de anillo de producción sin reiniciar toda la JVM?Código de recarga en un servidor ring-clojure de producción

Actualmente utilizo wrap-reload en producción, pero esto no funciona para mí porque a veces quiero ejecutar comandos en el repl (haciendo migraciones de bases de datos por ejemplo) antes de que ring comience a manejar peticiones con el nuevo código. También hay varios blogs y tutoriales que dicen que no se debe usar wrap-reload en la producción, aunque no entiendo por qué no.

He encontrado la siguiente solución, pero confieso que no entiendo muy bien lo que sucede bajo el capó. Me preguntaba si podría obtener un control de cordura por alguien que lo haga. ¿Esta técnica parece razonable?

La idea es tener una ruta (/ admin/reload-clj) que haga que se vuelva a cargar todo el código de clojure.

(defonce ^:dynamic *jetty*) 
(declare reload-clj) 

(defn app [req] 
... 
(when (= (req :uri) "/admin/reload-clj") (reload-clj req)) 
...) 

(defn start-jetty [] 
(let [j (run-jetty app {:port (http-port) :join? false :max-threads 16})] 
    (dosync (ref-set *jetty* j)) 
    j)) 

(defn reload-clj [req] 
(future 
    (log/info "Reloading clojure code...") 
    (require '(whrusrv admin main utils wdb) :reload-all) 
    (.stop @*jetty*) 
    (start-jetty) 
    (log/info "Clojure reload success!")) 
{:status 200 
    :headers {"Content-Type" "text/plain"} 
    :body "Reloading..."}) 

(defn -main [& args] 
(start-jetty)) 

Respuesta

6

El código que tiene funcionará, aunque debe tener en cuenta que :reload-all solo carga un espacio de nombres y las dependencias de espacios de nombres. No carga dependencias recursivamente de esos espacios de nombres.

Debo añadir que la recarga de esta manera no se recomienda en un sistema de producción.

El código implementado recientemente puede tener errores que no son evidentes hasta que se reinicia el sistema (por ejemplo, dependen de una var que todavía está definida desde el sistema en ejecución pero cuya declaración se eliminó). El sistema funcionará bien pero luego falla al reiniciar.

El código de carga también puede tener efectos secundarios que podrían dañar su entorno de producción. Aunque es un buen estilo para evitar esto, la única manera de estar seguro de que algo inesperado no sucederá es realizar un reinicio de JVM.

La mejor forma de realizar una implementación de tiempo de inactividad cero en la JVM es realizar un despliegue continuo utilizando un equilibrador de carga.

+1

Gracias por la explicación, ¡eso aclara las cosas! –

2

que tenían un servicio web de intranet en Common Lisp en un trabajo anterior, por lo que no sé si eso califica como producción. Además, al estar en CL, mi respuesta no es ni específica ni específica. Aún así, es útil para volver a cargar el código.

Lo que hice fue iniciar un servidor swank (parte de slime) en la instancia lisp de producción y conectarlo remotamente desde mi escritorio. De esa forma podría escribir código nuevo, reescribir código, depurar, recargar, etc. Obviamente, tendrá que ser muy cuidadoso en un sistema de producción real .

Puedes hacer lo mismo en clojure, incluso tienes alternativas para swank, como nrepl.

Deberá tener también en cuenta la seguridad y permitir solo las conexiones locales, o lo que sea que funcione para usted.

+0

Estoy muy interesado en soluciones alternativas también. Supongo que casi todo sería mejor que lo que tienes en Java (¿OSGi alguien?) – ivant

+0

Hago algo similar para mi servidor Java de nivel medio. He estado considerando probar repl en combinación con JMX. Creo que con algún truco debería ser posible enrutar todo el tráfico nrepl a través de JMX, que a su vez puede protegerse con SSL y autenticación. –

Cuestiones relacionadas