【问题标题】:Tomcat - starting webapps in a specific orderTomcat - 以特定顺序启动 webapps
【发布时间】:2012-02-06 07:27:32
【问题描述】:

我知道 Tomcat 和 Servlet 规范 do not support starting webapps in a particular order。

但是,在我看来,这似乎是一个常见的用例,我想知道是否有人发现了一个聪明的解决方法。

我有 webapp A,它使用 Spring Remoting 公开共享服务,其中 webapp B 是客户端。除非 webapp A 正在运行,否则 Webapp B 无法初始化。但是,我的 Tomcat 总是线性启动 webapps,从 webapp B 开始。

出于基础架构的原因,我必须让它们在同一个 Tomcat 服务器上运行。

有什么想法吗?

谢谢, 罗伊

更新 -

事实证明,在我的特殊情况下,顺序并不重要。原因是这样的:假设我使用以下方法之一在应用程序 B 之前启动应用程序 A。所以应用程序 A 启动,但是,由于 Spring 远程处理正在使用 HTTP Invoker,HTTP 端口尚未打开(它不会打开直到 所有 应用程序启动)。所以 A 将启动,B 将挂起,因为它正在寻找的端口尚不可用。呵呵。

最终结果是两个独立的 Tomcat 实例。

【问题讨论】:

  • 让需要等待其他网络应用的网络应用等待,而不是失败并放弃。
  • 嘿戴夫,unf。它们不会并行启动。B 启动(好吧,尝试启动),然后 A 将启动。但如果 B 因为无法连接到 A 而无法启动,则 A 永远不会尝试启动。
  • 它应该以 ASCII 顺序启动它们。我在 Debian Apache 安装中看到的一个技巧是创建 000_default、001_default 命名约定。这样在查看 dir 列表的 ASCII 顺序时会更清楚一些。
  • 评论更新。如果您的应用 A 依赖于另一个应用 B,那么您应该按照 here 或 here 的描述实现 Tomcat 启动生命周期监听器,并在收到 Tomcat AFTER_START_EVENT 后完成应用初始化。

标签: tomcat servlets


【解决方案1】:

我们有同样的问题,为了解决这个问题,我们依赖于应用程序按照它们在<tomcat_home>/conf/server.xml 中定义的顺序启动的事实(我知道这很棘手)。

这当然在server.xml 中对应用程序进行硬编码有一个缺点,但我们可以忍受。

【讨论】:

  • 谢谢 mindas - 这可能是“最好”的方法,但 unf 对我不起作用 - 我将编辑我的问题以解释原因。
【解决方案2】:

如果您不关心 hack 一些 tomcat 代码并创建自己的主机实例,这很容易实现

1) 创建一个 org.apache.catalina.core.StandardHost 的子类,比如 MyHost:

    class MyHost extends org.apache.catalina.core.StandardHost{
        public MyHost (){
        super();
        //changing HashMap for a predictable ordered Map :)
        this.children = new LinkedHashMap();
        }
    } 

2) 在你服务器的 xml Host 标签上注册你的类()

虽然看起来不可思议,但只要您在 Host 标签内以正确的顺序声明了所有 Web 应用程序,它就可以解决问题:

    <Host>
     <context app1>
     <context app2>
   </Host>

无论您使用哪种 SO,Thaen app1 都会在 app2 之前启动。

【讨论】:

  • 第一步之后,我们应该把 MyHost 类放在哪里?能否请您详细说明。
  • 能否提供主机配置示例
【解决方案3】:

这是 Linux 上的另一个技巧。

由于错误的 WSDL,我们的一些 Web 服务应用程序无法部署。如果在许多其他应用程序之后部署或启动它们,就会发生这种情况。 它们的启动顺序取决于在 /opt/apache-tomee/conf/Catalina/localhost 中找到上下文 xml 的顺序

可以使用“ls -1f”进行验证。一个普通的“ls”给出一个排序的输出。

这曾经是文件添加到该目录的顺序,但对于 ext4 文件系统,该顺序基于文件名的哈希值。这可以按如下方式禁用:

# tune2fs -O ^dir_index /dev/xyz

现在您至少可以自行决定它们的启动顺序。重新排序:将所有文件移至临时文件夹,然后按所需顺序移回。

【讨论】:

  • 刚刚在野外发现了这个技巧。谢谢你的解释!
  • 我不知道“脆弱性”。我以这种方式运行了一个托管 50 个应用程序的 Tomcat,并且没有无缘无故重启 Tomcat 的习惯。此外,我无意打破 Tomcat 本身的透明度。
【解决方案4】:

旧线程,但是...

解决此问题的另一种方法是创建一个自定义 HostConfig 类,该类按您需要的方式对 Web 应用程序进行排序。

public class OrderedHostConfig extends HostConfig {

    @Override
    protected String[] filterAppPaths(String[] unfilteredAppPaths) {
        String[] files = super.filterAppPaths(unfilteredAppPaths);
        Arrays.sort(files, compare());
        return files;
    }

    private Comparator<String> compare() {
        return (o1, o2) -> {
        // Your own implementation 
        };
    }
}

然后你可以在你的 server.xml 中的 Host 定义下引用这个类。

<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true" hostConfigClass="your.package.OrderedHostConfig">

你需要把它编译成一个 jar 并保存在 Tomcat 的 /lib 目录中。就我而言:

/var/lib/tomcat8/lib

我喜欢这种方法,因为:

  1. 仅在相关的地方强制执行该命令,但无需使用上下文在主机定义中手动管理所有 Web 应用

  2. 在代码库中搜索 .war 文件名也会引用这个类,这样更容易找到文件是否被重命名。

  3. 不更改 tomcat 8 的私有最终字段,请参阅 here

【讨论】:

  • 这实际上是一个很好的解决方案。在 Tomcat 8.5 和 9.x 上测试过,运行良好。
  • @mvreijn 你测试过吗?我不确定这是否适用于 9.x,至少对于 Tomcat v9.0.19 它不起作用。它也不在官方的 Tomcat API 文档中。
  • @wzona 是的,我已经如前所述进行了测试。但是,行为可能已经改变,或者可能是巧合。
【解决方案5】:

理论上,您可以在contextInitialized() 中通过ExecutorService 生成Runnable,这反过来会定期检查其他Web 应用程序的可用性(也许通过触发HTTP HEAD 请求?)。一旦另一个 web 应用程序可用,然后在 servlet 上下文中设置一些属性来表明这一点。添加一个Filter,它会检查该属性是否存在并相应地阻止/继续请求。

【讨论】:

    【解决方案6】:

    我知道这个问题有点老了,但我在尝试做同样的事情时发现了它,并认为我会用更好的解决方案进行更新......

    您可以在 server.xml 中定义多个服务,这些服务在不同的端口上运行。服务按照它们在 server.xml 中出现的顺序依次启动。这意味着您可以拥有 - 例如 - 在第一个服务中运行的配置服务,然后在第二个服务中运行依赖它的应用程序(我使用默认的 Catalina 为其余服务...)

    您可以在此处查看更多信息: http://wiki.apache.org/tomcat/FAQ/Miscellaneous#Q27

    这是我在 Catalina 服务之前包含的服务:

    <Service name="ConfigService">
        <Connector port="8081" protocol="HTTP/1.1"
            connectionTimeout="20000"
            redirectPort="8444" />
        <Engine name="ConfigServiceEngine" defaultHost="localhost">
            <Host name="localhost"  appBase="webapps"
                unpackWARs="true" autoDeploy="true"
                xmlValidation="false" xmlNamespaceAware="false">
    
                <Context path="/" reloadable="true" docBase="/path/to/your/service/directory" />
            </Host>
        </Engine>
    </Service>
    

    如您所见,我使用 docbase 而不是 appBase,但如果您愿意,您应该无法配置不同的 appBase...

    注意,更改服务和引擎的名称很重要。

    HTH

    【讨论】:

      【解决方案7】:

      这是我用来创建 2 个级别的 webapp 加载的一个很好的技巧。在每个级别中,不保证顺序。这依赖于 tomcat 将首先从 tomcat/conf/[Engine Name]/[Host Name] 加载上下文描述符,然后才从 server.xml 中 Host 元素的 appBase 属性加载上下文

      只需将以下代码添加到您想要在第二级(即稍后)加载的 webapp 中的某处

      File contextDescriptor = new File(getParameter("catalina.home"),"/conf/Catalina/localhost/mywebapp.xml");
      contextDescriptor.deleteOnExit();
      

      【讨论】:

        【解决方案8】:

        由于没有一个选项适用于 Tomcat 9.0.19(@Luiz 也提到过),我们使用代码方法并用最少的自定义实现替换了 Tomcat StandardHost 和 HostConfig:

        public class CustomTomcatHost extends StandardHost {
        
            public CustomTomcatHost() {
                super();
            }
        
            @Override
            public void addLifecycleListener(LifecycleListener listener) {
                if (listener instanceof HostConfig) {
                    listener = new OrderedHostConfig();
                }
                super.addLifecycleListener(listener);
            }
        }
        

        HostConfig 中的 deployApps 函数必须被覆盖才能对所有内容进行排序(包括 /webapps 文件夹中的 WAR 文件以及 configbase 文件夹中的描述符 XML 文件(例如 conf/Catalina/localhost)):

        public class OrderedHostConfig extends HostConfig {
        
            public OrderedHostConfig() {
                super();
            }
        
            public String[] prioritySort(String[] paths) {
                if (paths == null) return null;
        
                Arrays.sort(paths, new Comparator<String>() {
                    @Override
                    public int compare(String a, String b) {
                        return a.compareTo(b); //TODO: sort paths based on your criteria
                    }
                });
        
                return paths;
            }
        
            @Override
            protected void deployApps() {
        
                File appBase = host.getAppBaseFile();
                File configBase = host.getConfigBaseFile();
        
                String[] apps = prioritySort(filterAppPaths(appBase.list()));
        
                // Deploy XML descriptors from configBase
                deployDescriptors(configBase, prioritySort(configBase.list()));
                // Deploy WARs
                deployWARs(appBase, apps);
                // Deploy expanded folders
                deployDirectories(appBase, apps);
            }
        }
        

        然后我们将新类放在Tomcat /lib目录下的一个新jar文件中,并修改conf/server.xml文件,用我们自己的实现替换宿主类:

        <Host name="localhost"  appBase="webapps" unpackWARs="true" autoDeploy="true" className="com.example.CustomTomcatHost" >
        

        在启动时,Tomcat 会按要求的顺序加载所有组件。

        【讨论】:

          【解决方案9】:

          难道它们是按照部署顺序开始的吗?

          因此,如果我有四个应用程序:1、2、3、4,并且我按以下顺序部署它们:1、2、4(重启 tomcat),3(重启)。它会在某处缓存一些东西以使项目始终按该顺序启动?所以1、2、4、3。它发生在这里。我不确定,但也许它可以帮助某人。

          【讨论】:

            猜你喜欢
            • 2010-12-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-09-13
            • 2019-02-07
            • 2015-08-28
            • 2014-10-16
            相关资源
            最近更新 更多