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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/125409/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 11:06:50  来源:igfitidea点击:

How do you remotely update Java applications?

javarelease-management

提问by David Crow

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 应用程序(此处没有 Web 应用程序容器或 WAR 文件)。它只是一个自治的 Java 程序。

采纳答案by Kevin Day

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).

您没有指定服务器应用程序的类型 - 我将假设您没有运行网络应用程序(因为部署 WAR 已经完成了您所说的工作,并且您很少需要网络应用程序来执行拉取类型更新。如果您谈论的是 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):

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

  1. Consider having a bootstrap.jar file that is capable of reading a jnlp file and downloading required/updated jars prior to launching the application.

  2. JAR files canbe 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.

  3. 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:

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

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

  3. 即使可以覆盖 JAR,您也可能需要考虑对 lib 路径采用乒乓方法(如果您尚未将应用启动器配置为自动读取 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 是所需的类路径。

  1. 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.

  2. 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.

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

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

回答by Lars Westergren

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“包”,添加和删除它们。我自己没有使用过它,所以我不知道开源框架/服务器的质量,但这里有一堆有用的链接可以帮助您入门:

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

回答by Will Hartung

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),重命名新目录 (mv application_new application),然后重新启动它。

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.

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

回答by Redbeard

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

回答by bmeck

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 在其上运行时无法修改 Jar,这会导致错误。我尝试过类似的任务,我想出的最好的方法是制作更新后的 Jar 的副本并将启动脚本转换为查看该 Jar。一旦你有了更新的 Jar,启动它并等待旧的 Jar 在给它发出这样做的信号后结束。不幸的是,这意味着 GUI 等会丢失一秒钟,但是在 Java 中序列化大多数结构很容易,并且当前的 GUI 可以在实际关闭之前传输到更新的应用程序(尽管有些东西可能无法序列化!)。

回答by Andrew Swan

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 文件。我自己从未使用过它,但了解 Spring 产品组合的总体质量,我相信它值得一看。

回答by Lars A Fr?yland

We use Eclipse which the update system of OSGi, and our experience is very good.

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

Recommended!

受到推崇的!

回答by Thorbj?rn Ravn Andersen

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 允许在本地缓​​存中注入应用程序,而无需实际调用程序,并且可以将其标记为“离线”。由于缓存用于调用程序,因此只会在下次运行时更新。为此,您很可能需要名称中带有版本号的 jar(例如 our-library-2009-06-01.jar)。