如何让 Spring Boot 应用程序在 tomcat 失败时退出
Posted
技术标签:
【中文标题】如何让 Spring Boot 应用程序在 tomcat 失败时退出【英文标题】:How to make a Spring Boot application quit on tomcat failure 【发布时间】:2022-01-19 11:53:48 【问题描述】:我们有一堆基于 Spring Boot 2.5.4 的微服务,还包括 spring-kafka:2.7.6
和 spring-boot-actuator:2.5.4
。所有服务都使用 Tomcat 作为 servlet 容器并启用优雅关闭。这些微服务使用 docker 进行容器化。
由于配置错误,昨天我们在其中一个容器上遇到了问题,因为它占用了一个已经从另一个容器绑定的端口。
日志状态:
Stopping service [Tomcat]
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
***************************
APPLICATION FAILED TO START
***************************
Description:
Web server failed to start. Port 8080 was already in use.
但是,由于 kafka 消费者/流,JVM 仍在运行。
我需要销毁所有东西,或者至少做一个System.exit(error-code)
来触发 docker 重启策略。我怎么能做到这一点?如果可能,使用配置的解决方案比需要开发的解决方案更好。
我开发了一个由SpringBootApplication
和KafkaConsumer
类组成的最小测试应用程序,以确保问题与我们的微服务无关。结果一样。
POM 文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
卡夫卡监听器
@Component
public class KafkaConsumer
@KafkaListener(topics = "test", groupId = "test")
public void process(String message)
application.yml
spring:
kafka:
bootstrap-servers: kafka:9092
日志文件
2021-12-17 11:12:24.955 WARN 29067 --- [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Failed to start bean 'webServerStartStop'; nested exception is org.springframework.boot.web.server.PortInUseException: Port 8080 is already in use
2021-12-17 11:12:24.959 INFO 29067 --- [ main] o.apache.catalina.core.StandardService : Stopping service [Tomcat]
2021-12-17 11:12:24.969 INFO 29067 --- [ main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-12-17 11:12:24.978 ERROR 29067 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Web server failed to start. Port 8080 was already in use.
Action:
Identify and stop the process that's listening on port 8080 or configure this application to listen on another port.
2021-12-17 11:12:25.151 WARN 29067 --- [ntainer#0-0-C-1] org.apache.kafka.clients.NetworkClient : [Consumer clientId=consumer-test-1, groupId=test] Error while fetching metadata with correlation id 2 : test=LEADER_NOT_AVAILABLE
2021-12-17 11:12:25.154 INFO 29067 --- [ntainer#0-0-C-1] org.apache.kafka.clients.Metadata : [Consumer clientId=consumer-test-1, groupId=test] Cluster ID: NwbnlV2vSdiYtDzgZ81TDQ
2021-12-17 11:12:25.156 INFO 29067 --- [ntainer#0-0-C-1] o.a.k.c.c.internals.AbstractCoordinator : [Consumer clientId=consumer-test-1, groupId=test] Discovered group coordinator kafka:9092 (id: 2147483636 rack: null)
2021-12-17 11:12:25.159 INFO 29067 --- [ntainer#0-0-C-1] o.a.k.c.c.internals.AbstractCoordinator : [Consumer clientId=consumer-test-1, groupId=test] (Re-)joining group
2021-12-17 11:12:25.179 INFO 29067 --- [ntainer#0-0-C-1] o.a.k.c.c.internals.AbstractCoordinator : [Consumer clientId=consumer-test-1, groupId=test] (Re-)joining group
2021-12-17 11:12:27.004 INFO 29067 --- [ntainer#0-0-C-1] o.a.k.c.c.internals.AbstractCoordinator : [Consumer clientId=consumer-test-1, groupId=test] Successfully joined group with generation GenerationgenerationId=2, memberId='consumer-test-1-c5924ab5-afc8-4720-a5d7-f8107ace3aad', protocol='range'
2021-12-17 11:12:27.009 INFO 29067 --- [ntainer#0-0-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-test-1, groupId=test] Finished assignment for group at generation 2: consumer-test-1-c5924ab5-afc8-4720-a5d7-f8107ace3aad=Assignment(partitions=[test-0])
2021-12-17 11:12:27.021 INFO 29067 --- [ntainer#0-0-C-1] o.a.k.c.c.internals.AbstractCoordinator : [Consumer clientId=consumer-test-1, groupId=test] Successfully synced group in generation GenerationgenerationId=2, memberId='consumer-test-1-c5924ab5-afc8-4720-a5d7-f8107ace3aad', protocol='range'
2021-12-17 11:12:27.022 INFO 29067 --- [ntainer#0-0-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-test-1, groupId=test] Notifying assignor about the new Assignment(partitions=[test-0])
2021-12-17 11:12:27.025 INFO 29067 --- [ntainer#0-0-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-test-1, groupId=test] Adding newly assigned partitions: test-0
2021-12-17 11:12:27.029 INFO 29067 --- [ntainer#0-0-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-test-1, groupId=test] Found no committed offset for partition test-0
2021-12-17 11:12:27.034 INFO 29067 --- [ntainer#0-0-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-test-1, groupId=test] Found no committed offset for partition test-0
2021-12-17 11:12:27.040 INFO 29067 --- [ntainer#0-0-C-1] o.a.k.c.c.internals.SubscriptionState : [Consumer clientId=consumer-test-1, groupId=test] Resetting offset for partition test-0 to position FetchPositionoffset=0, offsetEpoch=Optional.empty, currentLeader=LeaderAndEpochleader=Optional[kafka:9092 (id: 11 rack: null)], epoch=0.
2021-12-17 11:12:27.045 INFO 29067 --- [ntainer#0-0-C-1] o.s.k.l.KafkaMessageListenerContainer : test: partitions assigned: [test-0]
【问题讨论】:
当正确集成时,这不应该发生。所以我想知道你是如何设置东西来听 kafka 的,看起来你是在 Spring(Boot)的范围/管理之外做的。 嗨@M.Deinum 我已经更新了我的问题,包括一个新启动应用程序的测试用例 【参考方案1】:既然你已经把所有东西都容器化了,那就更简单了。
只需使用 Spring Web 设置一个小型健康检查端点,用于查看服务器是否仍在运行,例如:
@RestController(
public class HealtcheckController
@Get("/monitoring")
public String getMonitoring()
return "200: OK";
然后在Dockerfile
的HEALTHCHECK
部分中引用它。如果服务器停止,则容器将被调度为unhealthy
,并重新启动:
FROM ...
ENTRYPOINT ...
HEALTHCHECK localhost:8080/monitoring
如果您不想开发任何东西,那么您可以使用任何其他您知道它应该成功回答为 HEALTCHECK
的端点,但我建议您明确地为此设置一个端点。
【讨论】:
感谢您提出的解决方案。在发布之前,我认为我们可以将actuator
(已包含)与它的健康端点一起使用,但如果可能的话,我想在 spring-boot 应用程序中解决它【参考方案2】:
我向 Spring kafka 的人们提出了一个问题,结果是一个错误。无需配置,spring 应用程序应按预期退出。参考:https://github.com/spring-projects/spring-kafka/issues/2055
正如问题中所述,修复是针对 spring-kafka 2.7.x、2.6.x、2.5.x 进行的。我将升级到第一个包含修复的 2.7.x 补丁版本。
【讨论】:
以上是关于如何让 Spring Boot 应用程序在 tomcat 失败时退出的主要内容,如果未能解决你的问题,请参考以下文章
如何让 Spring Boot 应用程序在 tomcat 失败时退出
如何让 Spring Boot 使用 MultiTenantSpringLiquibase?
如何让Javamelody使用不同的端口(Spring Boot+暴露的两个HTTP端口)
如何让我的 Spring Boot 应用程序使用给定的用户名和密码登录到 keycloak?
如何让IDEA像STS那样高效调试Spring Boot程序
如何让 Spring Boot Actuator LdapHealthIndicator 与 Spring Security 的 Ldap 一起运行?