【问题标题】:Efficiently deploy multiple instances of same WAR ( different contexts, same container )高效部署相同 WAR 的多个实例(不同的上下文,相同的容器)
【发布时间】:2012-04-28 07:43:28
【问题描述】:

我有一个 WAR(app.war)和一个容器(Tomcat、Jetty、Glassfish 等等)。 我的目标是按需在容器上部署数百个相同 Web 应用程序的实例。

http://foo/app1 --> app.war
http://foo/app2 --> app.war
http://foo/app3 --> app.war 
...
http://foo/appN --> app.war

实现这一目标的一些明显方法:

  • 在 Tomcat 中,为每个应用程序创建一个 context.xml 文件(名为 appN.xml ),所有文件都指向同一个 WAR。其他容器也有类似的方法
    • 这种方法的问题:会爆炸WAR N次,占用大量磁盘空间
  • 使用符号链接创建指向 app.war 扩展版本的 webapp/{app1,app2,appN} 文件夹。这可以防止磁盘空间爆炸,但 JVM 仍在将许多重复的 JAR 加载到内存中
  • 使用一些共享 lib 文件夹来包含大多数 jars(以及前两个选项的组合)。

我想知道是否有更好的方法来做到这一点。理想情况下,创建一个新实例不应该占用更多的磁盘空间(除了边缘配置文件),并且只占用与线程执行堆栈和其他运行时分配相关的内存。

有什么想法吗?

【问题讨论】:

  • 您会考虑将应用程序重写为多租户应用程序吗?如果有 100 个完全相同的 WAR 和代码的实例,我会考虑只设计 1 个将部署到根上下文的 WAR?
  • @beny23 详细的解释也可以帮助我处理我正在做的一些事情。你有机会提供一个吗?
  • 我在下面发布了一个答案,但是如果您告诉我们您为什么要这样做,我可能会发布一个更好的答案。
  • 感谢您的提示;)这是一种多租户设置。我需要为应该在自己的上下文中运行的每个用户提供一个“相同”的应用程序。以这种方式构建应用程序的原因超出了本文的范围,但有一个强大的商业案例推动了这一点。
  • 如果你给我们商业案例,我可能会有更多的建议。它与安全有关吗?不同的上下文无济于事。是每个用户都需要自己的 URL 路径吗?您可以通过 URL 重写来做到这一点。或者给每个用户一个子域(使用 servlet 过滤器很容易做到)。

标签: java tomcat glassfish jetty war


【解决方案1】:

Jetty 通过所谓的叠加层增加了对您不久前寻找的内容的支持。

http://wiki.eclipse.org/Jetty/Tutorial/Configuring_the_Jetty_Overlay_Deployer

从维基页面复制一点:

  • 您可以保持 WAR 文件不可变,甚至已签名,以便清楚您部署了哪个版本。
  • 您为自定义/配置 Web 应用程序所做的所有修改都是单独的 WAR,因此很容易识别,以便查看和迁移到新版本。
  • 您可以创建一个参数化模板叠加层,其中包含适用于 Web 应用程序的许多实例(例如,用于多租户部署)的常见自定义和配置。
  • 由于分层部署清楚地识别了通用组件和实例特定组件,Jetty 能够为模板共享类加载器和静态资源缓存,从而大大减少多个实例的内存占用。

【讨论】:

    【解决方案2】:

    抱歉,有点跑题了,但在我看来,您的场景大喊“多租户”应用程序,因此您有一个可以为多个“租户”(客户)服务的应用程序。

    关于多租户设置,必须考虑以下注意事项:

    多租户的好处:

    • 共享代码意味着为一个客户修复的错误对所有人都已修复(如果不同的客户对什么构成错误和什么构成功能有不同的看法,这也可能是一个缺点)。
    • 集群部署可以在客户之间分担负载(但是,需要确保所有客户都可以使用峰值容量)。

    缺点:

    • 代码会稍微复杂一些,因为查询需要确保客户之间的“区别”有效,而不会意外地将客户暴露给彼此的数据。

    【讨论】:

      【解决方案3】:

      您可以在前端配置 Apache (mod_proxy/mod_proxy_ajp) 以将命名虚拟主机指向部署在 Tomcat 上的单个 WAR。您的应用程序应该以服务所有请求的方式设计/编写——每个网站名称的特定配置可以存储在数据库中或作为应用程序中的配置文件——您的应用程序只需要探测用户的请求域名即可确保应用正确的设置(每个会话一次)。一般来说,您应该能够通过一个应用程序解决此问题。伟大的开发者都是懒惰的。

      【讨论】:

        【解决方案4】:

        如果您使用的是 Jetty,则可以通过编程方式添加上下文。

        WebAppContext webapp = new WebAppContext();
        webapp.setBaseResource(myBaseDirectory);
        webapp.setContextPath(myContextPath);
        

        只需为所有上下文循环执行此操作。它的磁盘空间开销应该接近于零。

        在 Tomcat 中可能有类似的方法。

        【讨论】:

        • 好的。我没有测试过这个。这会导致 WAR 被分解为“工作”文件夹吗?
        • 不,我不这么认为。如果是这样,它只会分解为一个文件夹,而不是每个上下文一个。
        【解决方案5】:

        如果这是用于实验,那么您列出的任何方法都可以工作。

        如果这是用于生产,那么我建议不要这样做。虽然我没有测试所有容器,但我使用过的容器让我相信,简单地为无头虚拟机提供容器会更有弹性。 Linux VM 可以非常小,使用 VM 技术,您可以根据需要添加或减少任意数量的实例。

        如果您真的想要一个动态增长的解决方案,那么您应该寻求消除单点故障,而不是尝试将您的整个世界集中在一起。

        如果您确实需要“高达第二”的负载扩展/收缩,那么您应该查看 AWS 或 CloundFoundry。

        【讨论】:

        • 是的,我们为每个用户/应用程序实例配置了一台机器。主要问题是成本。有些用户有一段时间不使用他们的应用程序,我想将他们移到“共享”机器上。也可以为每个实例生成一个 VM 进程,但我试图找出哪种方法开销较小:多个 VM/容器或每个容器多个实例。
        猜你喜欢
        • 2013-05-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-10-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多