我们有一个Java服务器应用程序,它运行在许多计算机上,所有计算机都连接到Internet,一些连接到防火墙。我们需要从中心站点远程更新JAR文件和启动脚本,而不会对应用程序本身造成明显的中断。
这个过程必须是无人值守和万无一失的(即我们无法承受因不合时宜的互联网中断而破坏应用程序)。
过去我们使用各种外部脚本和实用程序来处理类似的任务,但由于它们有自己的依赖关系,因此结果难以维护且便于携带。在制作新内容之前,我想从社区获得一些意见。
有没有人为此找到了一个好的解决方案?有任何想法或建议吗?
只是为了澄清:这个应用程序是一个服务器,但不适用于Web应用程序(此处没有webapp容器或WAR文件)。它只是一个自治的Java程序。
答案 0 :(得分:6)
您没有指定服务器应用程序的类型 - 我将假设您没有运行Web应用程序(因为部署WAR已经完成了您正在谈论的内容,并且您很少需要Web应用程序来如果您正在谈论一个Web应用程序,以下讨论仍然适用 - 您只需实现WAR文件的更新检查和乒乓而不是单个文件。
您可能想看看jnlp - WebStart基于此(这是一种客户端应用程序部署技术),但我非常确定它可以针对服务器类型应用程序执行更新而定制。无论如何,jnlp在提供可用于下载所需JAR所需版本的描述符方面做得相当不错......
对此有一些一般性的想法(我们在同一个桶中有几个应用程序,并且正在考虑自动更新机制):
考虑使用bootstrap.jar文件,该文件能够在启动应用程序之前读取jnlp文件并下载所需/更新的jar文件。
即使应用程序正在运行,也可以更新JAR文件 (至少在Windows上,这是最有可能对运行文件进行锁定的操作系统)。如果您使用自定义类加载器,或者您有一堆可能随时加载或卸载的JAR,您可能遇到问题,但如果您创建机制来防止这种情况,那么覆盖JAR然后重新启动应用程序应该是足以更新。
即使可以覆盖JAR,您也可以考虑使用ping路径的ping-pong方法(如果您还没有将应用程序启动器配置为自动读取所有jar文件) lib文件夹并自动将它们添加到类路径中,然后这就是你真正想做的事情)。以下是乒乓球的工作原理:
应用程序启动并查看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文件夹的中断更新)。最后,你重新启动应用程序,并且bootstrap选择了lib-pong是所需的类路径。
如上所述的乒乓球允许回滚。如果你正确地设计它,你可以拥有一个你测试的应用程序,然后永远不会更改检查,看它是否应该回滚给定的版本。这样,如果你搞砸并部署破坏应用程序的东西,你可以使版本无效。应用程序的这一部分只需要从坏的lib- *文件夹中删除version.properties文件,然后重新启动。保持这部分很简单是很重要的,因为这是你的安全故障。
你可以拥有2个以上的文件夹(而不是ping / pong,只需要lib-yyyymmdd并清除除最新的5之外的所有文件夹)。这允许更高级(但更复杂!)回滚JAR。
答案 1 :(得分:4)
你绝对应该看看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
答案 2 :(得分:3)
我建议Capistrano用于多服务器部署。虽然它是为部署Rails应用程序而构建的,但我已经看到它成功用于部署Java应用程序。
答案 3 :(得分:3)
我将使用ansible将jar分发到多个服务器并运行更新脚本。
要使用户看不到更新,应用程序必须停止旧实例并迅速启动一个新实例,而Java应用程序不是很好。
有一些方法可以使更新没有停机时间,但是没有一种是免费的。选择解决方案时,您应该考虑可以接受的停机时间以及可以接受的停机费用。
我看到两个选择:
正如Hartung所提到的,您还必须考虑可能要更新的外部依赖项,例如数据库架构。 为了获得无缝更新,您可能还希望对数据库进行滚动更新。它将要求您不要在应用程序的任何两个连续发行版之间引入任何重大更改。例如。当您要删除列时,在第一个版本中,您将删除应用程序中对该列的所有引用,而在下一个版本中,则可以删除数据库中的列。
答案 4 :(得分:2)
当JVM在其上运行时,无法修改Jars并导致错误。我已经尝试过类似的任务,我想出的最好的是复制更新的Jar并转换启动脚本以查看该Jar。一旦你有了更新的Jar,启动它并等待旧的Jar结束后给它发出信号。不幸的是,这意味着一段时间内GUI等的丢失,但序列化java中的大多数结构很容易,并且当前的GUI可以在实际关闭之前转移到更新的应用程序(有些东西可能不是可序列化的!)。
答案 5 :(得分:2)
使更新成为原子非常困难,特别是如果您要进行任何数据库更新。
但是,如果不这样做,您可以做的是首先确保您的应用程序可以从相对路径运行。也就是说,您可以将您的应用程序放在某个目录中,并找到相对于该位置的所有重要文件,这样您的实际安装位置就不那么重要了。
接下来,复制您的安装。现在,您拥有“正在运行”的版本,并且您拥有“新”版本。
使用您喜欢的任何技术更新“新”版本(FTP,rsync,纸带,无论你的船是什么漂浮)。
验证您的安装(校验和,快速单元测试,无论您需要什么 - 如果您愿意,甚至可以在测试端口上启动它)。
如果您对新安装感到满意,请按原始运行实例,重命名原始目录(mv application application_old),重命名新目录(mv application_new应用程序),然后重新启动它。
您的停机时间减少到服务器关闭和启动时间(因为重命名是“免费”)。
如果您偶然发现了一个严重错误,那么您的原始版本仍然存在。停止新服务器,重命名,重新启动旧服务器。很快就会退缩。
另一个好处是您的服务基础架构是静态的(如您的rc脚本,cron作业等),因为它们指向“应用程序”目录,并且它不会更改。
也可以使用软链接而不是重命名目录来完成。无论哪种方式都没问题。
但是,如果您的应用程序合作,该技术很简单,并且接近防弹。
现在,如果你有DB更改,那么,这是完全不同的令人讨厌的问题。理想情况下,如果您可以使数据库更改“向后兼容”,那么希望旧应用程序版本可以在新架构上运行,但这并非总是可行。
答案 6 :(得分:1)
如果您使用基于OSGi的应用服务器(例如SpringSource dm Server),我相信您可以热部署JAR文件。我自己从未使用它,但是知道Spring产品组合的一般质量,我相信它值得一看。
答案 7 :(得分:0)
我们使用OSGi更新系统的Eclipse,我们的经验非常好。
推荐!
答案 8 :(得分:0)
最新版本的Java Web Start允许在本地缓存中注入应用程序而无需实际调用程序,并且可以将其标记为“脱机”。由于缓存是用于调用程序的缓存,因此它将仅在下次运行时更新。为此,您很可能需要名称中包含版本号的罐子(例如our-library-2009-06-01.jar)。