在生产环 - clojure服务器上重新加载代码

时间:2012-04-03 04:50:40

标签: clojure ring

在不重新启动整个JVM的情况下,将新代码推送到生产环服务器的最佳方法是什么?

目前我在生产中使用wrap-reload,但这对我来说并不适用,因为有时我想在ring开始使用新代码处理请求之前在repl中运行命令(例如进行数据库迁移)。此外,各种博客和教程都说在生产中不使用换行重装,但我不明白为什么不这样做。

我已经提出了以下解决方案,但我承认我并不深入了解幕后发生的事情。我想知道我是否可以得到一个人的理智检查。这种技术看起来合理吗?

这个想法是有一个路径(/ admin / reload-clj)导致重新加载所有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))

2 个答案:

答案 0 :(得分:6)

您拥有的代码将起作用,但您应该知道:reload-all仅加载命名空间和命名空间依赖项。它不会递归加载那些名称空间的依赖项。

我应该补充说,以这种方式重新加载在生产系统中是非常强烈的。

新部署的代码可能具有在系统重新启动之前不明显的错误(例如,它们依赖于仍然从正在运行的系统定义但其声明已被删除的var)。系统运行正常但重启后失败。

加载代码也会产生副作用,这可能会破坏您的生产环境。虽然它避免了这些的好风格,但真正确保意外事件不会发生的唯一方法是重新启动JVM。

在JVM上进行零停机部署的最佳方法是使用负载均衡器进行滚动部署。

答案 1 :(得分:2)

我在以前的工作中使用普通lisp的Intranet Web服务,因此我不知道这是否符合生产要求。另外,在CL中我的答案既不是响铃也不是特定的。不过,它对重新加载代码很有用。

我所做的是在生产lisp实例上启动一个swank服务器(slime的一部分),甚至从我的桌面远程连接到它。这样我就可以编写新代码,重写代码,调试,重新加载等等。显然,你必须在真正的生产系统中格外小心。

你可以在clojure中做同样的事情,你甚至可以选择swank,比如nrepl。

您还必须考虑安全性,并且只允许本地连接或任何适合您的连接。