We've got a Java server application that runs on a number of computers, all connected to the Internet, some behind firewalls. We need to remotely update the JAR files and startup scripts from a central site, with no noticeable interruption to the app itself.
我们有一个Java服务器应用程序,它运行在许多计算机上,都连接到Internet,一些连接到防火墙。我们需要从*站点远程更新JAR文件和启动脚本,而不会对应用程序本身造成明显的中断。
The process has to be unattended and foolproof (i.e. we can't afford to break the app due to untimely internet outages).
这个过程必须是无人值守和万无一失的(即我们无法承受由于不合时宜的互联网中断而破坏应用程序)。
In the past we've used a variety of external scripts and utilities to handle similar tasks, but because they have their own dependencies, the result is harder to maintain and less portable. Before making something new, I want to get some input from the community.
在过去,我们使用各种外部脚本和实用程序来处理类似的任务,但由于它们有自己的依赖关系,因此结果难以维护且便于携带。在制作新内容之前,我想从社区获得一些意见。
Has anyone found a good solution for this already? Got any ideas or suggestions?
有人找到了一个很好的解决方案吗?有任何想法或建议吗?
Just to clarify: This app is a server, but not for web applications (no webapp containers or WAR files here). It's just an autonomous Java program.
只是为了澄清:这个应用程序是一个服务器,但不适用于Web应用程序(此处没有webapp容器或WAR文件)。它只是一个自治的Java程序。
8 个解决方案
#1
6
You didn't specify the type of server apps - I'm going to assume that you aren't running web apps (as deploying a WAR already does what you are talking about, and you very rarely need a web app to do pull type updates. If you are talking about a web app, the following discussion can still apply - you'll just implement the update check and ping-pong for the WAR file instead of individual files).
您没有指定服务器应用程序的类型 - 我将假设您没有运行Web应用程序(因为部署WAR已经完成了您正在谈论的内容,并且您很少需要Web应用程序来执行拉动类型如果您正在讨论Web应用程序,以下讨论仍然适用 - 您只需实施WAR文件的更新检查和乒乓而不是单个文件。
You may want to take a look at jnlp - WebStart is based on this (this is a client application deployment technology), but I'm pretty sure that it could be tailored to performing updates for a server type app. Regardless, jnlp does a pretty good job of providing descriptors that can be used for downloading required versions of required JARs...
您可能想看一下jnlp - WebStart基于此(这是一种客户端应用程序部署技术),但我非常确定它可以针对服务器类型应用程序执行更新而定制。无论如何,jnlp在提供可用于下载所需JAR所需版本的描述符方面做得相当不错......
Some general thoughts on this (we have several apps in the same bucket, and are considering an auto-update mechanism):
关于此的一些一般想法(我们在同一个桶中有几个应用程序,并正在考虑自动更新机制):
-
Consider having a bootstrap.jar file that is capable of reading a jnlp file and downloading required/updated jars prior to launching the application.
考虑使用bootstrap.jar文件,该文件能够在启动应用程序之前读取jnlp文件并下载所需/更新的jar文件。
-
JAR files can be updated even while an app is running (at least on Windows, and that is the OS most likely to hold locks on running files). You can run into problems if you are using custom class loaders, or you have a bunch of JARs that might be loaded or unloaded at any time, but if you create mechanisms to prevent this, then overwriting JARs then re-launching the app should be sufficient for update.
即使在应用程序运行时,JAR文件也可以更新(至少在Windows上,这是最有可能对运行文件进行锁定的操作系统)。如果您使用自定义类加载器,或者您有一堆可能随时加载或卸载的JAR,您可能遇到问题,但如果您创建机制来防止这种情况,那么覆盖JAR然后重新启动应用程序应该是足以更新。
-
Even though it is possible to overwrite JARs, you might want to consider a ping-pong approach for your lib path (if you don't already have your app launcher configured to auto-read all jar files in the lib folder and add them to the class path automatically, then that's something you really do want to do). Here's how ping-pong works:
尽管可以覆盖JAR,但您可能需要考虑使用ping路径的ping-pong方法(如果您尚未将应用程序启动器配置为自动读取lib文件夹中的所有jar文件并将其添加到类路径自动,然后你真正想做的事情)。以下是乒乓球的工作原理:
App launches and looks at lib-ping\version.properties and lib-pong\version.properties and determines which is newer. Let's say that lib-ping has a later version. The launcher searches for lib-ping*.jar and adds those files to the CP during the launch. When you do an update, you download jar files into lib-pong (or copy jar files from lib-ping if you want to save bandwidth and the JAR didn't actually change - this is rarely worth the effort, though!). Once you have all JARs copied into lib-pong, the very last thing you do is create the version.properties file (that way an interrupted update that results in a partial lib folder can be detected and purged). Finally, you re-launch the app, and bootstrap picks up that lib-pong is the desired classpath.
应用程序启动并查看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是所需的类路径。
-
ping-pong as described above allows for a roll-back. If you design it properly, you can have one piece of your app that you test the heck out of and then never change that checks to see if it should roll-back a given version. That way if you do mess up and deploy something that breaks the app, you can invalidate the version. This part of the application just has to delete the version.properties file from the bad lib-* folder, then re-launch. It's important to keep this part dirt simple because it's your fail safe.
如上所述的乒乓球允许回滚。如果你正确地设计它,你可以拥有一个你测试的应用程序,然后永远不会更改检查,看它是否应该回滚给定的版本。这样,如果您搞砸并部署破坏应用程序的内容,则可以使版本无效。应用程序的这一部分只需要从坏的lib- *文件夹中删除version.properties文件,然后重新启动。重要的是要保持这部分简单,因为这是你的安全故障。
-
You can have more than 2 folders (instead of ping/pong, just have lib-yyyymmdd and purge all but the newest 5, for example). This allows for more advanced (but more complicated!) rollback of JARs.
您可以拥有2个以上的文件夹(而不是ping / pong,只需要lib-yyyymmdd并清除除最新的5之外的所有文件夹)。这允许更高级(但更复杂!)回滚JAR。
#2
4
You should definitely take a look at OSGi, it was created just for these cases (especially for embedded products) and is used by a large number of companies. You can update jar "bundles", add and remove them, while the app is running. I haven't used it myself, so I don't know about the quality of the open source frameworks/servers, but here is a bunch of useful links to get you started:
你应该看看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
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
#3
3
I'd recommend Capistrano for multi-server deployment. While it is built for deploying Rails apps, I've seen it used successfully to deploy Java applications.
我建议Capistrano用于多服务器部署。虽然它是为部署Rails应用程序而构建的,但我已经看到它成功用于部署Java应用程序。
Link: Capistrano 2.0 Not Just for Rails
链接:Capistrano 2.0不只是为了Rails
#4
2
Jars cannot be modified while the JVM is running on top of it and will result in errors. I have tried similar tasks and the best I came up with is making a copy of the updated Jar and transition the start up script to look at that Jar. Once you have the updated Jar, start it up and wait on the old Jar to end after giving it the signal to do so. Unfortunately this means a loss of GUI etc. for a sec but serializing most of the structures in java is easy and the current GUI could be transferred to the updated application before actually closing (some things may not be serializable though!).
JVM在其上运行时无法修改Jars并将导致错误。我已经尝试过类似的任务,我想出的最好的是制作更新的Jar的副本并转换启动脚本以查看该Jar。一旦你有了更新的Jar,启动它并等待旧的Jar结束后给它发出信号。不幸的是,这意味着一段时间内GUI等的丢失,但序列化java中的大多数结构很容易,并且当前的GUI可以在实际关闭之前转移到更新的应用程序(有些事情可能不是可序列化的!)。
#5
2
It's very hard to make the update atomic, especially if you have any database updating to do.
使更新成为原子非常困难,特别是如果您要进行任何数据库更新。
But, if you don't, what you can do is first make sure your application can run from a relative path. That is, you can put your app in some directory, and all of the important files are found relative to that location, so that that your actual installation location is not really important.
但是,如果不这样做,您可以做的是首先确保您的应用程序可以从相对路径运行。也就是说,您可以将应用程序放在某个目录中,并找到相对于该位置的所有重要文件,这样您的实际安装位置就不是很重要了。
Next, duplicate your installation. Now, you have the "running" version and you have the "new" version.
接下来,复制您的安装。现在,您拥有“正在运行”的版本,并且您拥有“新”版本。
Update the "new" version using whatever tech you like (FTP, rsync, paper tape, whatever floats your boat).
使用您喜欢的任何技术更新“新”版本(FTP,rsync,纸带,无论您的船是什么漂浮)。
Verify your installation (checksums, quick unit tests, whatever you need -- even start it up on a test port if you like).
验证您的安装(校验和,快速单元测试,无论您需要什么 - 如果您愿意,甚至可以在测试端口上启动它)。
When you are happy with the new installation, down the original running instance, RENAME the original directory (mv application application_old), rename the NEW directory (mv application_new application), and start it back up.
当您对新安装感到满意时,在原始运行实例中,重命名原始目录(mv application application_old),重命名NEW目录(mv application_new应用程序),然后重新启动它。
Your down time is reduced to server shut down and start up time (since rename is "free").
您的停机时间减少到服务器关闭和启动时间(因为重命名是“免费”)。
If, by chance, you detect a critical error, you have your original version still there. Stop the new server, rename it back, restart the old. Very fast fall back.
如果您偶然发现了一个严重错误,那么您的原始版本仍然存在。停止新服务器,重命名,重新启动旧服务器。很快回落。
The other nice thing is that your service infrastructure is static (like your rc scripts, cron jobs, etc.) since they point to the "application" directory, and it doesn't change.
另一件好事是您的服务基础架构是静态的(如您的rc脚本,cron作业等),因为它们指向“应用程序”目录,并且它不会更改。
It can also be done with soft links instead of renaming directories. either way is fine.
它也可以使用软链接而不是重命名目录来完成。无论哪种方式都很好。
But the technique is simple, and near bullet proof if your application cooperates.
但是这项技术很简单,并且如果您的应用程序合作,则接近防弹。
Now, if you have DB changes, well, that's completely different nasty issue. Ideally if you can make your DB changes "backward compatible", then hopefully the old application version can run on the new schema, but that's not always possible.
现在,如果你有DB更改,那么,这是完全不同的令人讨厌的问题。理想情况下,如果您可以使数据库更改“向后兼容”,那么希望旧的应用程序版本可以在新模式上运行,但这并不总是可行的。
#6
1
I believe you can hot-deploy JAR files if you use an OSGi-based app server like SpringSource dm Server. I've never used it myself, but knowing the general quality of the Spring portfolio, I'm sure it's worth a look.
如果您使用基于OSGi的应用服务器(如SpringSource dm Server),我相信您可以热部署JAR文件。我自己从未使用它,但是知道Sprin*品组合的一般质量,我相信它值得一看。
#7
0
We use Eclipse which the update system of OSGi, and our experience is very good.
我们使用OSGi更新系统的Eclipse,我们的经验非常好。
Recommended!
#8
0
The latest version of Java Web Start allows for injecting an application in the local cache without actually invoking the program and it can be marked as "offline". Since the cache is what is used to invoke the program, it will be updated for the next run only. For this to work you will most likely need jars with version numbers in their name (e.g. our-library-2009-06-01.jar).
最新版本的Java Web Start允许在本地缓存中注入应用程序而无需实际调用程序,并且可以将其标记为“脱机”。由于缓存是用于调用程序的缓存,因此它将仅在下次运行时更新。为此,您很可能需要名称中包含版本号的罐子(例如our-library-2009-06-01.jar)。
#1
6
You didn't specify the type of server apps - I'm going to assume that you aren't running web apps (as deploying a WAR already does what you are talking about, and you very rarely need a web app to do pull type updates. If you are talking about a web app, the following discussion can still apply - you'll just implement the update check and ping-pong for the WAR file instead of individual files).
您没有指定服务器应用程序的类型 - 我将假设您没有运行Web应用程序(因为部署WAR已经完成了您正在谈论的内容,并且您很少需要Web应用程序来执行拉动类型如果您正在讨论Web应用程序,以下讨论仍然适用 - 您只需实施WAR文件的更新检查和乒乓而不是单个文件。
You may want to take a look at jnlp - WebStart is based on this (this is a client application deployment technology), but I'm pretty sure that it could be tailored to performing updates for a server type app. Regardless, jnlp does a pretty good job of providing descriptors that can be used for downloading required versions of required JARs...
您可能想看一下jnlp - WebStart基于此(这是一种客户端应用程序部署技术),但我非常确定它可以针对服务器类型应用程序执行更新而定制。无论如何,jnlp在提供可用于下载所需JAR所需版本的描述符方面做得相当不错......
Some general thoughts on this (we have several apps in the same bucket, and are considering an auto-update mechanism):
关于此的一些一般想法(我们在同一个桶中有几个应用程序,并正在考虑自动更新机制):
-
Consider having a bootstrap.jar file that is capable of reading a jnlp file and downloading required/updated jars prior to launching the application.
考虑使用bootstrap.jar文件,该文件能够在启动应用程序之前读取jnlp文件并下载所需/更新的jar文件。
-
JAR files can be updated even while an app is running (at least on Windows, and that is the OS most likely to hold locks on running files). You can run into problems if you are using custom class loaders, or you have a bunch of JARs that might be loaded or unloaded at any time, but if you create mechanisms to prevent this, then overwriting JARs then re-launching the app should be sufficient for update.
即使在应用程序运行时,JAR文件也可以更新(至少在Windows上,这是最有可能对运行文件进行锁定的操作系统)。如果您使用自定义类加载器,或者您有一堆可能随时加载或卸载的JAR,您可能遇到问题,但如果您创建机制来防止这种情况,那么覆盖JAR然后重新启动应用程序应该是足以更新。
-
Even though it is possible to overwrite JARs, you might want to consider a ping-pong approach for your lib path (if you don't already have your app launcher configured to auto-read all jar files in the lib folder and add them to the class path automatically, then that's something you really do want to do). Here's how ping-pong works:
尽管可以覆盖JAR,但您可能需要考虑使用ping路径的ping-pong方法(如果您尚未将应用程序启动器配置为自动读取lib文件夹中的所有jar文件并将其添加到类路径自动,然后你真正想做的事情)。以下是乒乓球的工作原理:
App launches and looks at lib-ping\version.properties and lib-pong\version.properties and determines which is newer. Let's say that lib-ping has a later version. The launcher searches for lib-ping*.jar and adds those files to the CP during the launch. When you do an update, you download jar files into lib-pong (or copy jar files from lib-ping if you want to save bandwidth and the JAR didn't actually change - this is rarely worth the effort, though!). Once you have all JARs copied into lib-pong, the very last thing you do is create the version.properties file (that way an interrupted update that results in a partial lib folder can be detected and purged). Finally, you re-launch the app, and bootstrap picks up that lib-pong is the desired classpath.
应用程序启动并查看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是所需的类路径。
-
ping-pong as described above allows for a roll-back. If you design it properly, you can have one piece of your app that you test the heck out of and then never change that checks to see if it should roll-back a given version. That way if you do mess up and deploy something that breaks the app, you can invalidate the version. This part of the application just has to delete the version.properties file from the bad lib-* folder, then re-launch. It's important to keep this part dirt simple because it's your fail safe.
如上所述的乒乓球允许回滚。如果你正确地设计它,你可以拥有一个你测试的应用程序,然后永远不会更改检查,看它是否应该回滚给定的版本。这样,如果您搞砸并部署破坏应用程序的内容,则可以使版本无效。应用程序的这一部分只需要从坏的lib- *文件夹中删除version.properties文件,然后重新启动。重要的是要保持这部分简单,因为这是你的安全故障。
-
You can have more than 2 folders (instead of ping/pong, just have lib-yyyymmdd and purge all but the newest 5, for example). This allows for more advanced (but more complicated!) rollback of JARs.
您可以拥有2个以上的文件夹(而不是ping / pong,只需要lib-yyyymmdd并清除除最新的5之外的所有文件夹)。这允许更高级(但更复杂!)回滚JAR。
#2
4
You should definitely take a look at OSGi, it was created just for these cases (especially for embedded products) and is used by a large number of companies. You can update jar "bundles", add and remove them, while the app is running. I haven't used it myself, so I don't know about the quality of the open source frameworks/servers, but here is a bunch of useful links to get you started:
你应该看看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
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
#3
3
I'd recommend Capistrano for multi-server deployment. While it is built for deploying Rails apps, I've seen it used successfully to deploy Java applications.
我建议Capistrano用于多服务器部署。虽然它是为部署Rails应用程序而构建的,但我已经看到它成功用于部署Java应用程序。
Link: Capistrano 2.0 Not Just for Rails
链接:Capistrano 2.0不只是为了Rails
#4
2
Jars cannot be modified while the JVM is running on top of it and will result in errors. I have tried similar tasks and the best I came up with is making a copy of the updated Jar and transition the start up script to look at that Jar. Once you have the updated Jar, start it up and wait on the old Jar to end after giving it the signal to do so. Unfortunately this means a loss of GUI etc. for a sec but serializing most of the structures in java is easy and the current GUI could be transferred to the updated application before actually closing (some things may not be serializable though!).
JVM在其上运行时无法修改Jars并将导致错误。我已经尝试过类似的任务,我想出的最好的是制作更新的Jar的副本并转换启动脚本以查看该Jar。一旦你有了更新的Jar,启动它并等待旧的Jar结束后给它发出信号。不幸的是,这意味着一段时间内GUI等的丢失,但序列化java中的大多数结构很容易,并且当前的GUI可以在实际关闭之前转移到更新的应用程序(有些事情可能不是可序列化的!)。
#5
2
It's very hard to make the update atomic, especially if you have any database updating to do.
使更新成为原子非常困难,特别是如果您要进行任何数据库更新。
But, if you don't, what you can do is first make sure your application can run from a relative path. That is, you can put your app in some directory, and all of the important files are found relative to that location, so that that your actual installation location is not really important.
但是,如果不这样做,您可以做的是首先确保您的应用程序可以从相对路径运行。也就是说,您可以将应用程序放在某个目录中,并找到相对于该位置的所有重要文件,这样您的实际安装位置就不是很重要了。
Next, duplicate your installation. Now, you have the "running" version and you have the "new" version.
接下来,复制您的安装。现在,您拥有“正在运行”的版本,并且您拥有“新”版本。
Update the "new" version using whatever tech you like (FTP, rsync, paper tape, whatever floats your boat).
使用您喜欢的任何技术更新“新”版本(FTP,rsync,纸带,无论您的船是什么漂浮)。
Verify your installation (checksums, quick unit tests, whatever you need -- even start it up on a test port if you like).
验证您的安装(校验和,快速单元测试,无论您需要什么 - 如果您愿意,甚至可以在测试端口上启动它)。
When you are happy with the new installation, down the original running instance, RENAME the original directory (mv application application_old), rename the NEW directory (mv application_new application), and start it back up.
当您对新安装感到满意时,在原始运行实例中,重命名原始目录(mv application application_old),重命名NEW目录(mv application_new应用程序),然后重新启动它。
Your down time is reduced to server shut down and start up time (since rename is "free").
您的停机时间减少到服务器关闭和启动时间(因为重命名是“免费”)。
If, by chance, you detect a critical error, you have your original version still there. Stop the new server, rename it back, restart the old. Very fast fall back.
如果您偶然发现了一个严重错误,那么您的原始版本仍然存在。停止新服务器,重命名,重新启动旧服务器。很快回落。
The other nice thing is that your service infrastructure is static (like your rc scripts, cron jobs, etc.) since they point to the "application" directory, and it doesn't change.
另一件好事是您的服务基础架构是静态的(如您的rc脚本,cron作业等),因为它们指向“应用程序”目录,并且它不会更改。
It can also be done with soft links instead of renaming directories. either way is fine.
它也可以使用软链接而不是重命名目录来完成。无论哪种方式都很好。
But the technique is simple, and near bullet proof if your application cooperates.
但是这项技术很简单,并且如果您的应用程序合作,则接近防弹。
Now, if you have DB changes, well, that's completely different nasty issue. Ideally if you can make your DB changes "backward compatible", then hopefully the old application version can run on the new schema, but that's not always possible.
现在,如果你有DB更改,那么,这是完全不同的令人讨厌的问题。理想情况下,如果您可以使数据库更改“向后兼容”,那么希望旧的应用程序版本可以在新模式上运行,但这并不总是可行的。
#6
1
I believe you can hot-deploy JAR files if you use an OSGi-based app server like SpringSource dm Server. I've never used it myself, but knowing the general quality of the Spring portfolio, I'm sure it's worth a look.
如果您使用基于OSGi的应用服务器(如SpringSource dm Server),我相信您可以热部署JAR文件。我自己从未使用它,但是知道Sprin*品组合的一般质量,我相信它值得一看。
#7
0
We use Eclipse which the update system of OSGi, and our experience is very good.
我们使用OSGi更新系统的Eclipse,我们的经验非常好。
Recommended!
#8
0
The latest version of Java Web Start allows for injecting an application in the local cache without actually invoking the program and it can be marked as "offline". Since the cache is what is used to invoke the program, it will be updated for the next run only. For this to work you will most likely need jars with version numbers in their name (e.g. our-library-2009-06-01.jar).
最新版本的Java Web Start允许在本地缓存中注入应用程序而无需实际调用程序,并且可以将其标记为“脱机”。由于缓存是用于调用程序的缓存,因此它将仅在下次运行时更新。为此,您很可能需要名称中包含版本号的罐子(例如our-library-2009-06-01.jar)。