您如何远程更新Java应用程序?

我们有一个Java服务器应用程序,它运行在许多计算机上,所有计算机都连接到Internet,一些连接到防火墙。 我们需要从中央站点远程更新JAR文件和启动脚本,而不会对应用程序本身造成明显的中断。

这个过程必须是无人值守和万无一失的(即我们无法承受因不合时宜的互联网中断而破坏应用程序)。

在过去,我们使用各种外部脚本和实用程序来处理类似的任务,但由于它们有自己的依赖关系,因此结果难以维护且便于携带。 在制作新内容之前,我想从社区获得一些意见。

有人找到了一个很好的解决方案吗? 有任何想法或建议吗?

只是为了澄清:这个应用程序是一个服务器,但不适用于Web应用程序(此处没有webapp容器或WAR文件)。 它只是一个自治的Java程序。

您没有指定服务器应用程序的类型 – 我将假设您没有运行Web应用程序(因为部署WAR已经完成了您正在谈论的内容,并且您很少需要Web应用程序来执行拉动类型如果您正在谈论Web应用程序,以下讨论仍然适用 – 您只需实现WAR文件的更新检查和乒乓而不是单个文件。

您可能想看一下jnlp – WebStart基于此(这是一种客户端应用程序部署技术),但我非常确定它可以针对服务器类型应用程序执行更新而定制。 无论如何,jnlp在提供可用于下载所需JAR所需版本的描述符方面做得相当不错……

关于此的一些一般想法(我们在同一个桶中有几个应用程序,并正在考虑自动更新机制):

  1. 考虑使用bootstrap.jar文件,该文件能够在启动应用程序之前读取jnlp文件并下载所需/更新的jar文件。

  2. 即使在应用程序运行时可以更新JAR文件(至少在Windows上,这是最有可能对运行文件进行锁定的操作系统)。 如果您使用自定义类加载器,或者您有一堆可能随时加载或卸载的JAR,您可能遇到问题,但如果您创建机制来防止这种情况,那么覆盖JAR然后重新启动应用程序应该是足以更新。

  3. 尽管可以覆盖JAR,但您可能需要考虑使用ping路径的ping-pong方法(如果您尚未将应用程序启动器配置为自动读取lib文件夹中的所有jar文件并将其添加到类路径自动,然后你真正想做的事情)。 以下是乒乓球的工作原理:

应用程序启动并查看lib-ping \ version.properties和lib-pong \ version.properties并确定哪个更新。 假设lib-ping有更高版本。 启动程序搜索lib-ping * .jar并在启动期间将这些文件添加到CP。 当你进行更新时,你可以将jar文件下载到lib-pong中(或者如果你想节省带宽而从lib-ping复制jar文件,而JAR实际上没有改变 – 尽管这很费劲!)。 将所有JAR复制到lib-pong后,最后要做的就是创建version.properties文件(这样可以检测并清除导致部分lib文件夹的中断更新)。 最后,重新启动应用程序,然后引导程序选择lib-pong是所需的类路径。

  1. 如上所述的乒乓球允许回滚。 如果你正确地设计它,你可以拥有一个你测试的应用程序,然后永远不会更改检查,看它是否应该回滚给定的版本。 这样,如果您搞砸并部署破坏应用程序的内容,则可以使版本无效。 应用程序的这一部分只需要从坏的lib- *文件夹中删除version.properties文件,然后重新启动。 重要的是要保持这部分简单,因为这是你的安全故障。

  2. 您可以拥有2个以上的文件夹(而不是ping / pong,只需要lib-yyyymmdd并清除除最新的5之外的所有文件夹)。 这允许更高级(但更复杂!)回滚JAR。

你一定要看看OSGi,它是为这些情况(特别是嵌入式产品)创建的,并被许多公司使用。 您可以在应用程序运行时更新jar“bundles”,添加和删除它们。 我自己没有使用它,所以我不知道开源框架/服务器的质量,但这里有一堆有用的链接可以帮助你入门:

http://www.osgi.org/Main/HomePage
http://www.aqute.biz/Code/Bnd
http://blog.springsource.com/2008/02/18/creating-osgi-bundles/
http://blog.springsource.com/
http://www.knopflerfish.org/
http://felix.apache.org/site/index.html

我建议Capistrano用于多服务器部署。 虽然它是为部署Rails应用程序而构建的,但我已经看到它成功用于部署Java应用程序。

链接: Capistrano 2.0不只是为了Rails

JVM在其上运行时无法修改Jars并将导致错误。 我已经尝试过类似的任务,我想出的最好的是复制更新的Jar并转换启动脚本以查看该Jar。 一旦你有了更新的Jar,启动它并等待旧的Jar结束后给它发出信号。 不幸的是,这意味着一段时间内GUI等的丢失,但序列化java中的大多数结构很容易,并且当前的GUI可以在实际关闭之前转移到更新的应用程序(有些事情可能不是可序列化的!)。

使更新成为primefaces非常困难,特别是如果您要进行任何数据库更新。

但是,如果不这样做,您可以做的是首先确保您的应用程序可以从相对路径运行。 也就是说,您可以将您的应用程序放在某个目录中,并找到相对于该位置的所有重要文件,这样您的实际安装位​​置就不那么重要了。

接下来,复制您的安装。 现在,您拥有“正在运行”的版本,并且您拥有“新”版本。

使用您喜欢的任何技术更新“新”版本(FTP,rsync,纸带,无论您的船是什么漂浮)。

validation您的安装(校验和,快速unit testing,无论您需要什么 – 如果您愿意,甚至可以在测试端口上启动它)。

当您对新安装感到满意时,在原始运行实例中,重命名原始目录(mv application application_old),重命名NEW目录(mv application_new应用程序),然后重新启动它。

您的停机时间减少到服务器关闭和启动时间(因为重命名是“免费”)。

如果您偶然发现了一个严重错误,那么您的原始版本仍然存在。 停止新服务器,重命名,重新启动旧服务器。 很快回落。

另一件好事是您的服务基础架构是静态的(如您的rc脚本,cron作业等),因为它们指向“应用程序”目录,并且它不会更改。

它也可以使用软链接而不是重命名目录来完成。 无论哪种方式都很好。

但是这项技术很简单,并且如果您的应用程序合作,则接近防弹。

现在,如果你有DB更改,那么,这是完全不同的令人讨厌的问题。 理想情况下,如果您可以使数据库更改“向后兼容”,那么希望旧的应用程序版本可以在新架构上运行,但这并不总是可行的。

如果您使用基于OSGi的应用服务器(如SpringSource dm Server),我相信您可以热部署JAR文件。 我自己从未使用它,但是知道Spring产品组合的一般质量,我相信它值得一看。

我们使用OSGi更新系统的Eclipse,我们的经验非常好。

推荐的!

最新版本的Java Web Start允许在本地缓​​存中注入应用程序而无需实际调用程序,并且可以将其标记为“脱机”。 由于缓存是用于调用程序的缓存,因此它将仅在下次运行时更新。 为此,您很可能需要在其名称中包含版本号的jar子(例如our-library-2009-06-01.jar)。