当前位置:   article > 正文

SpringBoot2.X优雅停机_spring boot 2.x.x 关闭endpoint

spring boot 2.x.x 关闭endpoint

优雅停机主要应用在版本更新的时候,为了等待正在工作的线程全部执行完毕,然后再停止。
这就涉及到了springboot的核心机制之一——Actuator。

Actuator

概念:actuator是制造术语,是指用于移动或控制某些物体的机械设备。 actuators可以通过很小的变化产生大量的运动。
这是官方给出的actuator的定义,正常人一看,根本看不懂嘛,总的来说,actuator就是个监控神器。

Endpoints

actuator的核心,它能够对应用程序进行监控及交互,springboot提供了大量的内置的endpoints(我们也可以自己扩展endpoint)。
下面列举了一些内置的endpoints:

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

IDDescriptionEnabled by default
heapdump返回一个hprof堆转储文件。Yes
jolokia通过HTTP公开JMX bean(当Jolokia位于类路径上时,WebFlux不能使用它)。Yes
logfile返回日志文件的内容(如果是log .file.name或log .file)。已经设置了路径属性)。支持使用HTTP范围头来检索日志文件的部分内容。Yes
prometheus以可以被Prometheus服务器抓取的格式公开度量数据。Yes
pom.xml中引入actuator依赖
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  • 1
  • 2
  • 3
  • 4
配置文件中开启shutdown endpoint

上面的表格可以看到,只有shutdown endpoint默认是关闭的,所以要先启用它。
application.yml:

management:
  endpoint:
    shutdown:
      enabled: true
  • 1
  • 2
  • 3
  • 4

application.properties:

#启用shutdown
management.endpoint.shutdown.enabled=true
  • 1
  • 2
完整代码

配置类:

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

        }
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
测试代码

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

    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

启动类:

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

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

测试方法:启动项目,浏览器访问http://localhost:8080/hello/world,多次刷新页面,控制台打出如下log:
在这里插入图片描述
然后通过在curl输入

curl -X POST http://localhost:8081/monitor/shutdown
  • 1

curl工具下载链接

关闭项目。控制台打出如下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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/334222
推荐阅读
相关标签
  

闽ICP备14008679号