Tomcat - 以特定顺序启动 Web 应用程序

发布于 2024-12-25 16:02:40 字数 676 浏览 1 评论 0原文

我知道 Tomcat 和 Servlet 规范 不支持在特定的环境下启动 webapp订单

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

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

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

有什么想法吗?

谢谢, Roy

更新 -

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

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

I know that Tomcat and the Servlet spec do not support starting webapps in a particular order.

However, this seems to me like a common use case, and I'm wondering if someone has discovered a clever workaround for it.

I have webapp A which uses Spring Remoting to expose a shared service, of which webapp B is a client. Webapp B cannot initialize unless webapp A is running. However, my Tomcat is always starting the webapps linearly, starting with webapp B.

For infrastructure reasons I have to have these running on the same Tomcat server.

Any ideas?

Thanks,
Roy

UPDATE -

Turns out that in my particular case, order doesn't matter. The reason is this: say I use one of the methods below to start app A before app B. So app A starts, but, since Spring remoting is using the HTTP Invoker, the HTTP port is not yet open (it won't open until all apps are started). So A will start, and B will hang, because the port it's looking for is not yet available. Doh.

End result was two separate Tomcat instances.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

遇见了你 2025-01-01 16:02:41

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

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

Can it be that they are starting in the order as they are deployed in?

So if i have four apps: 1, 2, 3, 4 and i deploy them in the following order: 1, 2, 4 (restart tomcat), 3 (restart). That it caches something somewhere to make the projects always startup in that order? So 1, 2, 4, 3. It happens over here. I'm unsure, but maybe it helps someone.

枫以 2025-01-01 16:02:40

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

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

We have the same problem and to solve it we're relying on the fact (slippery, I know) that applications are started in the order they are defined in <tomcat_home>/conf/server.xml.

This of course has a disadvantage of hardcoding apps in the server.xml but we can live with it.

最后的乘客 2025-01-01 16:02:40

如果您不介意修改一些 tomcat 代码并创建自己的 Host 实例,那么这很容易实现:

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 tag ()

看起来令人难以置信,只要您在 Host 标记内以正确的顺序声明所有 Web 应用程序,它就可以解决问题:

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

Thaen app1 将在 app2 之前启动,无论如何你用过哪个SO。

That's quite easy to achieve if you don't care hacking a bit of tomcat code and creating your own Host instance

1) Create a subClass of org.apache.catalina.core.StandardHost, say MyHost:

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

2) register your class on your server's xml Host tag ()

Incredible as it may seem, it solves the problem as long as you have all your web app declared in the correct order inside of Host tag:

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

Thaen app1 will start before app2, no matter which SO you used.

超可爱的懒熊 2025-01-01 16:02:40

这是 Linux 上的另一个技巧。

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

。可以使用“ls -1f”进行验证。简单的“ls”给出排序的输出。

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

# tune2fs -O ^dir_index /dev/xyz

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

Here is another trick on Linux.

Some of our webservice applications fail to deploy, because of erroneous WSDL. This happens if they are deployed or started after a number of other applications.
The order in which they are started depends on the order in which context xml's are found in /opt/apache-tomee/conf/Catalina/localhost

Can be verified using "ls -1f". A plain "ls" gives a sorted output.

This used to be the order in which files were added to that directory, but with ext4 filesystems, the order is based on a hash of the filename. This can be disabled as follows:

# tune2fs -O ^dir_index /dev/xyz

Now you can at least decide yourself in which order they will be started. Reordering: move all files to a temporary folder, move them back in the desired sequence.

木森分化 2025-01-01 16:02:40

旧线程,但是...

解决此问题的另一种方法是创建一个自定义 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 
        };
    }
}

然后,您可以在 Host 定义下的 server.xml 中引用此类。

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

您需要将其编译成 jar 并将其保存在 Tomcat 的 /lib 目录中。就我而言:

/var/lib/tomcat8/lib

我喜欢这种方法,因为:

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

  2. 在代码库中搜索 .war 文件名也会引用此类,这使得更容易查找该文件是否为

  3. 不更改 tomcat 8 的私有最终字段,请参阅此处< /a>

Old thread, but...

Another way to work around this is to create a custom HostConfig class which sorts the web apps the way you need.

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 
        };
    }
}

Then you can reference this class in your server.xml under the Host definition.

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

You need to compile this into a jar and save it in Tomcat's /lib directory. In my case:

/var/lib/tomcat8/lib

I like this method because:

  1. The order is enforced only where it's relevant, but there is no need to manage all the webapps manually in Host definitions using Context

  2. Searching for the .war file name in the codebase will also reference this class, which makes it easier to find if the file is renamed.

  3. No changing of private final fields for tomcat 8, see here

绝不放开 2025-01-01 16:02:40

由于没有一个选项适用于 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);
    }
}

中的 deployApps 函数>HostConfig 必须被重写才能对所有内容进行排序(包括 /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 按所需顺序加载所有组件。

Since none of the options worked for Tomcat 9.0.19 (the one mentioned by @Luiz also) we used the code approach and replaced the Tomcat StandardHost and HostConfig with minimal custom implementations:

public class CustomTomcatHost extends StandardHost {

    public CustomTomcatHost() {
        super();
    }

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

The deployApps function in HostConfig had to be overriden for sorting to work for everything (incl. WAR files in /webapps folder and also for descriptor XML files in configbase folder (e.g. 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);
    }
}

Then we placed the new classes in a new jar file in Tomcat /lib directory and modified conf/server.xml file to replace the host class with our own implementation:

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

During start Tomcat loaded all components in the required order.

三寸金莲 2025-01-01 16:02:40

理论上,您可以通过 contextInitialized() 中的 ExecutorService 生成一个 Runnable,后者会按时间间隔检查其他 Web 应用程序的可用性(可能通过触发 HTTP HEAD 请求?)。一旦其他 Web 应用程序可用,则在 servlet 上下文中设置一些属性来指示这一点。添加一个Filter来检查该属性是否存在并相应地阻止/继续请求。

In theory you could spawn a Runnable by ExecutorService in contextInitialized() which in turn checks the availability of the other webapp at timed intervals (perhaps by firing a HTTP HEAD request?). Once the other webapp is available, then set some attribute in the servlet context which indicates that. Add a Filter which checks for the presence of that attribute and blocks/continues requests accordingly.

泪意 2025-01-01 16:02:40

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

您可以在 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...

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

华泰

I know this question is a bit old, but I found it when trying to do the same thing and thought I'd update with a better solution...

You can define mulitple services in your server.xml, which run on different ports. The services are started sequentially according to the order they appear in the server.xml. This means that you can have - for example - a configuration service running in the first service and then the apps that depend on it in the second (I use the default Catalina one for the rest of them...)

You can see more info here:
http://wiki.apache.org/tomcat/FAQ/Miscellaneous#Q27

And this is the service that I include before the Catalina Service:

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

As you can see, I use docbase rather than appBase, but you should be able t configure a different appBase if you prefer...

NB it's important to change the name of both the service and the engine.

HTH

时光清浅 2025-01-01 16:02:40

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

只需在以下位置添加以下代码:您想要在第二级(即稍后)加载的网络应用程序

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

Here is a nice trick i use to create 2 levels of webapp loading. in each level the order is not guaranteed. This relies on the fact that tomcat will load first context descriptors from tomcat/conf/[Engine Name]/[Host Name] and only then contexts from the appBase attribute of the Host element in server.xml

Just add the following code somewhere in the webapp you would like to load in the 2nd level ( i.e. later )

File contextDescriptor = new File(getParameter("catalina.home"),"/conf/Catalina/localhost/mywebapp.xml");
contextDescriptor.deleteOnExit();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文