Tomcat 如果 spring-boot 上下文部署失败,多上下文部署将停止整个过程
Tomcat multi-context deployment stops whole process if spring-boot context fails to deploy
我有一个非常具体的 Tomcat 配置,我不想更改,我正在为 这个具体的 配置寻找解决方案。
我已经在 conf/server.xml 文件中声明了多个上下文 (<Context>
) 用于部署 Tomcat,不推荐我知道的东西。我的问题是,如果其中一个部署失败,整个 Tomcat 进程就会停止,从而终止已成功启动的其余应用程序。这种行为是预期的吗?是否有属性或方法来防止这种情况并模拟类似于 context.xml
文件的用法的不同上下文声明的行为?
我得到的错误如下:
ConfigServletWebServerApplicationContext : Exception encountered during context initialization
使用此堆栈跟踪(针对 Tomcat 版本 8.5.24):
SEVERE [Catalina-startStop-1] org.apache.catalina.core.ContainerBase.startInternal A child container failed during start
java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/auth-service]]
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:939)
at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:872)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1419)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1409)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/auth-service]]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:167)
... 6 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'authenticationController': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'app.client-html.context' in value "${app.client-html.context}"
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:405)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1415)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:608)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:531)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean[=12=](AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:925)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:588)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:144)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:767)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:426)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:326)
at org.springframework.boot.web.servlet.support.SpringBootServletInitializer.run(SpringBootServletInitializer.java:173)
at org.springframework.boot.web.servlet.support.SpringBootServletInitializer.createRootApplicationContext(SpringBootServletInitializer.java:153)
at org.springframework.boot.web.servlet.support.SpringBootServletInitializer.onStartup(SpringBootServletInitializer.java:95)
at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:174)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5196)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
... 6 more
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'app.client-html.context' in value "${app.client-html.context}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:178)
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:124)
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:239)
at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:210)
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.lambda$processProperties[=12=](PropertySourcesPlaceholderConfigurer.java:175)
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:931)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1308)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1287)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399)
... 27 more
示例配置:
<Host name="localhost" unpackWARs="true" autoDeploy="true">
<Context docBase="../../app-that-works" path="/itworks"/>
<Context docBase="../../app-that-fails" path="/itfails"/>
</Host>
重要提示:失败 应用程序是一个 spring-boot 应用程序,它被打包为 .war 文件和 运行 在香草 tomcat, 而非 嵌入式 tomcat 方式。这很重要,因为这些应用程序似乎具有这种行为...
免责声明:我指的是 vanilla Tomcat、not spring boot 或 embedded tomcat 等。我不是在寻找这样的东西解决方案,只是针对这个特定问题的解决方案。
基本上:
- 在
server.xml
中配置的 任何 组件的 start
阶段(参见 LifeCycle)异常导致服务器退出。然而,一些异常(如失败的 ServletContextListener
)不会传播。
- 在运行时添加的组件错误,不会导致服务器退出。
如果您不希望整个服务器停止,当上下文无法启动时,使用自动部署:为 server.xml
中定义的每个上下文创建一个包含内容的文件 $CATALINA_BASE/conf/<engine_name>/<host_name>/<context_path>.xml
:
<Context docBase="/path/to/application" />
在您的情况下,您需要创建 $CATALINA_BASE/conf/Catalina/localhost/itworks.xml
和 $CATALINA_BASE/conf/Catalina/localhost/itfails.xml
。有关 Tomcat 对 XML 描述符的命名约定,请参见 Naming。
编辑:您的堆栈跟踪表明我们可能正在处理 Tomcat 或 Spring 错误:
一方面,StandardContext
期望 ServletContainerInitializer
仅抛出 ServletException
s(参见 source code) and let's all unchecked exceptions through. Remark that at the same time all exceptions from ServletContextListener
s are caught and just logged (cf. source code)。
另一方面Spring的SpringServletContainerInitializer
应该将所有未经检查的异常包装到 ServletException
.
在您的情况下发生的情况是未经检查的异常一直到 StandardContext
并作为 LifecycleException
停止服务器重新抛出。无论如何,设置:
<Context throwOnFailure="false" ... />
应该阻止 Tomcat 传播异常。
我有一个非常具体的 Tomcat 配置,我不想更改,我正在为 这个具体的 配置寻找解决方案。
我已经在 conf/server.xml 文件中声明了多个上下文 (<Context>
) 用于部署 Tomcat,不推荐我知道的东西。我的问题是,如果其中一个部署失败,整个 Tomcat 进程就会停止,从而终止已成功启动的其余应用程序。这种行为是预期的吗?是否有属性或方法来防止这种情况并模拟类似于 context.xml
文件的用法的不同上下文声明的行为?
我得到的错误如下:
ConfigServletWebServerApplicationContext : Exception encountered during context initialization
使用此堆栈跟踪(针对 Tomcat 版本 8.5.24):
SEVERE [Catalina-startStop-1] org.apache.catalina.core.ContainerBase.startInternal A child container failed during start
java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/auth-service]]
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:939)
at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:872)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1419)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1409)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/auth-service]]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:167)
... 6 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'authenticationController': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'app.client-html.context' in value "${app.client-html.context}"
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:405)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1415)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:608)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:531)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean[=12=](AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:925)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:588)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:144)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:767)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:426)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:326)
at org.springframework.boot.web.servlet.support.SpringBootServletInitializer.run(SpringBootServletInitializer.java:173)
at org.springframework.boot.web.servlet.support.SpringBootServletInitializer.createRootApplicationContext(SpringBootServletInitializer.java:153)
at org.springframework.boot.web.servlet.support.SpringBootServletInitializer.onStartup(SpringBootServletInitializer.java:95)
at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:174)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5196)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
... 6 more
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'app.client-html.context' in value "${app.client-html.context}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:178)
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:124)
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:239)
at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:210)
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.lambda$processProperties[=12=](PropertySourcesPlaceholderConfigurer.java:175)
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:931)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1308)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1287)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399)
... 27 more
示例配置:
<Host name="localhost" unpackWARs="true" autoDeploy="true">
<Context docBase="../../app-that-works" path="/itworks"/>
<Context docBase="../../app-that-fails" path="/itfails"/>
</Host>
重要提示:失败 应用程序是一个 spring-boot 应用程序,它被打包为 .war 文件和 运行 在香草 tomcat, 而非 嵌入式 tomcat 方式。这很重要,因为这些应用程序似乎具有这种行为...
免责声明:我指的是 vanilla Tomcat、not spring boot 或 embedded tomcat 等。我不是在寻找这样的东西解决方案,只是针对这个特定问题的解决方案。
基本上:
- 在
server.xml
中配置的 任何 组件的start
阶段(参见 LifeCycle)异常导致服务器退出。然而,一些异常(如失败的ServletContextListener
)不会传播。 - 在运行时添加的组件错误,不会导致服务器退出。
如果您不希望整个服务器停止,当上下文无法启动时,使用自动部署:为 server.xml
中定义的每个上下文创建一个包含内容的文件 $CATALINA_BASE/conf/<engine_name>/<host_name>/<context_path>.xml
:
<Context docBase="/path/to/application" />
在您的情况下,您需要创建 $CATALINA_BASE/conf/Catalina/localhost/itworks.xml
和 $CATALINA_BASE/conf/Catalina/localhost/itfails.xml
。有关 Tomcat 对 XML 描述符的命名约定,请参见 Naming。
编辑:您的堆栈跟踪表明我们可能正在处理 Tomcat 或 Spring 错误:
一方面,
StandardContext
期望ServletContainerInitializer
仅抛出ServletException
s(参见 source code) and let's all unchecked exceptions through. Remark that at the same time all exceptions fromServletContextListener
s are caught and just logged (cf. source code)。另一方面Spring的
SpringServletContainerInitializer
应该将所有未经检查的异常包装到ServletException
.
在您的情况下发生的情况是未经检查的异常一直到 StandardContext
并作为 LifecycleException
停止服务器重新抛出。无论如何,设置:
<Context throwOnFailure="false" ... />
应该阻止 Tomcat 传播异常。