赞
踩
优雅停机主要应用在版本更新的时候,为了等待正在工作的线程全部执行完毕,然后再停止。
这就涉及到了springboot的核心机制之一——Actuator。
概念:actuator是制造术语,是指用于移动或控制某些物体的机械设备。 actuators可以通过很小的变化产生大量的运动。
这是官方给出的actuator的定义,正常人一看,根本看不懂嘛,总的来说,actuator就是个监控神器。
actuator的核心,它能够对应用程序进行监控及交互,springboot提供了大量的内置的endpoints(我们也可以自己扩展endpoint)。
下面列举了一些内置的endpoints:
ID | Description | Enabled by default |
---|---|---|
auditevents | 显示当前应用程序的审计事件信息。 | Yes,需要一个AuditEventRepository Bean |
beans | 显示应用程序中所有Spring bean的完整列表。 | Yes |
caches | 显示可用的缓存 | Yes |
conditions | 显示在配置和自动配置类上被评估的条件,以及它们匹配或不匹配的原因。 | Yes |
configprops | 显示所有@ConfigurationProperties的排序列表。 | Yes |
env | 从Spring的ConfigurableEnvironment中公开属性。 | Yes |
flyway | 显示已应用的所有Flyway数据库迁移。 | Yes |
health | 显示应用程序的健康信息。 | Yes |
httptrace | 显示HTTP跟踪信息(默认情况下,最后100个HTTP请求-响应交换)。 | Yes,需要一个AuditEventRepository Bean |
info | 显示任意应用程序信息。 | Yes |
integrationgraph | 显示Spring集成图。 | Yes |
loggers | 显示和修改应用程序中的记录器配置。 | Yes |
liquibase | 显示已应用的所有Liquibase数据库迁移。 | Yes |
metrics | 显示当前应用程序的“度量”信息。 | Yes |
mappings | 显示所有@RequestMapping路径的列表。 | Yes |
scheduledtasks | 显示应用程序中计划的任务。 | Yes |
sessions | 允许从Spring会话支持的会话存储中检索和删除用户会话。当使用Spring Session对反应性web应用程序的支持时不可用。 | Yes |
shutdown | 让应用程序优雅地关闭。 | No |
threaddump | 执行线程转储。 | Yes |
当你的应用是一个web应用的时候,诸如Spring MVC、Spring WebFlux或者Jersey等,可以使用下面的附加endpoint:
ID | Description | Enabled by default |
---|---|---|
heapdump | 返回一个hprof堆转储文件。 | Yes |
jolokia | 通过HTTP公开JMX bean(当Jolokia位于类路径上时,WebFlux不能使用它)。 | Yes |
logfile | 返回日志文件的内容(如果是log .file.name或log .file)。已经设置了路径属性)。支持使用HTTP范围头来检索日志文件的部分内容。 | Yes |
prometheus | 以可以被Prometheus服务器抓取的格式公开度量数据。 | Yes |
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
上面的表格可以看到,只有shutdown endpoint默认是关闭的,所以要先启用它。
application.yml:
management:
endpoint:
shutdown:
enabled: true
application.properties:
#启用shutdown
management.endpoint.shutdown.enabled=true
配置类:
package com.example.demo.Configuration; import org.apache.catalina.connector.Connector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.servlet.server.ServletWebServerFactory; import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.event.ContextClosedEvent; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * @ClassName ActuratorConfig * @Description garceful shutdown * @Author domi * @Date 2019/11/01 **/ @Configuration public class ActuratorConfig { private static final int waitTime = 20; //ms @Bean public GracefulShutdown gracefulShutdown() { return new GracefulShutdown(); } @Bean public ServletWebServerFactory servletContainer() { TomcatServletWebServerFactory tomcatServletWebServerFactory = new TomcatServletWebServerFactory(); tomcatServletWebServerFactory.addConnectorCustomizers(gracefulShutdown()); return tomcatServletWebServerFactory; } private static class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> { private static final Logger log = LoggerFactory.getLogger(GracefulShutdown.class); private volatile Connector connector; @Override public void customize(Connector connector) { this.connector = connector; } @Override public void onApplicationEvent(ContextClosedEvent contextClosedEvent) { this.connector.pause(); Executor executor = this.connector.getProtocolHandler().getExecutor(); if (executor instanceof ThreadPoolExecutor) { try { ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor; log.info("shutdown has started!"); threadPoolExecutor.shutdown(); log.info("shutdown has ended!"); if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) { log.warn("Tomcat 进程在" + waitTime + "秒内无法结束,尝试强制结束ing..."); } log.info("shutdown is success!"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } catch (Exception e) { e.printStackTrace(); } } } } }
application.yml:
info: app: name: spring-boot-actuator version: V1.0.0 test: test management: endpoint: shutdown: enabled: true endpoints: web: exposure: include: "*" base-path: /monitor server: port: 8081 server: port: 8080
Controller:
package com.example.demo.Contorller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.atomic.AtomicInteger; /** * @ClassName HomeController * @Description 模拟正在处理的多个线程 * @Author domi * @Date 2019/11/01 **/ @RestController @RequestMapping("/hello") public class HomeController { //计数器 public AtomicInteger started = new AtomicInteger(); public AtomicInteger ended = new AtomicInteger(); @RequestMapping(value = "/world", method = RequestMethod.GET) public String testThread() { System.out.println("[" + Thread.currentThread().getName() + "]{" + this + "}started:" + started.addAndGet(1)); try { Thread.sleep(1000 * 30); } catch (Exception ex) { ex.printStackTrace(); } System.out.println("[" + Thread.currentThread().getName() + "]{" + this + "}finished:" + ended.addAndGet(1)); return "hello"; } }
启动类:
package com.example.demo;
import com.example.demo.Contorller.HomeController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
测试方法:启动项目,浏览器访问http://localhost:8080/hello/world,多次刷新页面,控制台打出如下log:
然后通过在curl输入
curl -X POST http://localhost:8081/monitor/shutdown
关闭项目。控制台打出如下log:
如果在awaitTermination设定的时间内,还是有线程没有关闭的话,将会强制结束线程。
参考链接:
Spring Boot官方文档:https://docs.spring.io/spring-boot/docs/2.2.0.RELEASE/reference/html/production-ready-features.html
Spring Boot 1.X和2.X优雅重启实战:https://www.cnblogs.com/bigbigwood/p/9977069.html
Shut down embedded servlet container gracefully:https://github.com/spring-projects/spring-boot/issues/4657
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。