zoukankan      html  css  js  c++  java
  • spring boot不同版本的优雅关闭(graceful shutdown)和在windows下winsw服务方式运行的配置

    起因

    • spring boot默认是不会优雅关闭的,这样就导致在重启时会将正在运行的程序打断,导致故障发生。

    当前解决方式

    • 引入spring-boot-starter-actuator监控类库,它其中一个功能支持优雅关闭。
    • spring boot 2.3版本开始,自己集成了优雅关闭,无需再引入上方类库即可实现优雅关闭。

    坑爹的地方

    • spring-boot-starter-actuator文档中说是支持优雅关闭,但仅仅是spring层面上的,不和tomcat等容器挂钩,直到spring boot 2.3开启自带的优雅关闭后才真正能实现,也就是说2.3之前的版本根本实现不了优雅关闭,需要自己来进一步按照使用的容器做处理才行,参考这个issue:Allow the embedded web server to be shut down gracefully
    • 2.3以上版本,如果是在linux下,发送命令kill -2 xxx可以触发优雅关闭,但是在windows下,只有ctrl+c才能触发(可以在idea下用run里面的exit按钮模拟),但windows下我们一般是用服务来运行,所以永远无法触发ctrl+c,因此即便是2.3版本后,windows下也必须安装spring-boot-starter-actuator来通过发送http请求来实现优雅关闭。

    2.3以上版本的处理方法(如果是用tomcat容器需要9.0.33以上)

    • application.yml中增加:
      # 开启优雅关闭
      server:
        shutdown: graceful
      # 配置强制结束时间,不配置的话默认30s
      spring:
        lifecycle:
          timeout-per-shutdown-phase: 30s
      
    • 配置好后就支持优雅关闭了,linux端只需要在systemctl的配置文件中设置关闭命令是kill -2 xxxPID,pid可以通过文件或命令根据端口查找什么的,手头上暂没有linux来测试,后期有了后补上完整脚本。
    • widows端虽然也支持,但是如果用服务方式运行是没法触发ctrl+c相同的效果的,所以还是不行。

    2.3以下的处理方法(或者是2.3版本以上的windows端)

    • 引入spring-boot-starter-actuator的maven类库
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
      </dependency>
      
    • application.yml中增加:
      #监控相关配置
      management:
        endpoint:
          # 开启
          shutdown:
            enabled: true
        endpoints:
          web:
            # 只允许shutdown,为了安全,其它想要监控自行配置
            exposure:
              include: "shutdown"
            # 自定义请求路径,为了安全
            base-path: /xxx
        server:
          #自定义请求端口,为了安全
          port: 7080
      
      发送请求的路径是这样的:curl -X POST http://localhost:自定义端口/自定义路径/shutdown,由于路径和端口都是自定义的,所以安全性方面不用太过担心。
    • 分支1:如果是2.3版本以上的windows端,再开启自带的优雅关闭,就可以通过http请求来实现了
      # 开启优雅关闭
      server:
        shutdown: graceful
      # 配置强制结束时间,不配置的话默认30s
      spring:
        lifecycle:
          timeout-per-shutdown-phase: 30s
      
    • 分支2:如果是2.3以下版本,此时虽然可以发送http请求来关闭,但实际上不会等待正在执行的程序,而是会直接关闭,还应该配置容器相关,以tomcat容器为例:
      • 创建相关类:
          package xxx.xxx.xxx;
        
          import lombok.extern.slf4j.Slf4j;
          import org.apache.catalina.connector.Connector;
          import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
          import org.springframework.context.ApplicationListener;
          import org.springframework.context.event.ContextClosedEvent;
        
          import java.util.concurrent.Executor;
          import java.util.concurrent.ThreadPoolExecutor;
          import java.util.concurrent.TimeUnit;
        
          /**
           * 优雅关闭
           */
          @Slf4j
          public class GracefulShutdown implements TomcatConnectorCustomizer,
                  ApplicationListener<ContextClosedEvent> {
        
              private volatile Connector connector;
        
              /**
               * 30s强制关闭
               */
              private static final int TIMEOUT = 30;
        
              /**
               * 自定义链接
               *
               * @param connector
               */
              @Override
              public void customize(Connector connector) {
                  this.connector = connector;
              }
        
              /**
               * 关闭时触发
               *
               * @param event
               */
              @Override
              public void onApplicationEvent(ContextClosedEvent event) {
                  this.connector.pause();
                  Executor executor = this.connector.getProtocolHandler().getExecutor();
                  if (executor instanceof ThreadPoolExecutor) {
                      try {
                          ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
                          threadPoolExecutor.shutdown();
                          if (!threadPoolExecutor.awaitTermination(TIMEOUT, TimeUnit.SECONDS)) {
                              log.warn("Tomcat thread pool did not shut down gracefully within "
                                      + "30 seconds. Proceeding with forceful shutdown");
                          }
                      } catch (InterruptedException ex) {
                          Thread.currentThread().interrupt();
                      }
                  }
              }
          }
        
        • 在启动类中引入:
          package xx.xxx.xxx;
        
          import org.springframework.beans.factory.annotation.Qualifier;
          import org.springframework.boot.SpringApplication;
          import org.springframework.boot.autoconfigure.SpringBootApplication;
          import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
          import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
          import org.springframework.context.annotation.Bean;
        
          @SpringBootApplication
          public class XxxxApplication {
        
              /**
               * 优雅关闭bean
               * @return
               */
              @Bean("gracefulShutdown")
              public GracefulShutdown gracefulShutdown() {
                  return new GracefulShutdown();
              }
        
              /**
               * tomcat配置优雅关闭
               * @param gracefulShutdown
               * @return
               */
              @Bean
              public ConfigurableServletWebServerFactory webServerFactory(@Qualifier("gracefulShutdown") GracefulShutdown gracefulShutdown) {
                  TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
                  factory.addConnectorCustomizers(gracefulShutdown);
                  return factory;
              }
        
              public static void main(String[] args) {
                  SpringApplication.run(XxxxApplication .class, args);
              }
          }
        
      • 以上配置后再发送http请求才会优雅关闭,但仅适用于tomcat容器,undertow容器可以参考这个:Spring boot 2.0 之优雅停机

    windows端用winsw设置为服务运行时的配置

    • winsw可以从这里下载:winsw,主要作用就是可以让程序以服务的方式后台运行并能开机启动等
    • 需要下载windows下的curl,地址:curl for Windows
    • 配置winsw的config文件:
      <service>
        <!-- ID of the service. It should be unique across the Windows system-->
        <id>XXX</id>
        <!-- Display name of the service -->
        <name>xxx</name>
        <!-- Service description -->
        <description>xxx名称(powered by WinSW)</description>
        
        <!-- Path to the executable, which should be started -->
        <executable>java</executable>
        <startarguments>-jar -Xms128m -Xmx512m "D:\jar包路径\xxx.jar"</startarguments>
        <!--停止 -->
        <stopexecutable>D:\curl路径\bin\curl.exe</stopexecutable>
        <stoparguments>-X POST http://localhost:7080/xxx/shutdown</stoparguments>
        <!--不配置的话默认15s-->
        <stoptimeout>30 sec</stoptimeout>
        <startmode>Automatic</startmode>
        <logmode>none</logmode>
      </service>
      

    结束

    • 在windows下服务方式运行通过http来发送请求关闭有个缺点,如果应用正在启动中的时候发送了关闭请求,那关闭请求是失败的,但服务并不知道你是失败的,所以会卡住,应用还是会正常启动成功,此时只能成功后再手动发送一次请求关闭,或者是用sc queryex 服务名称来找到pid,然后调用taskkill /PID 查询到的pid /F来强制关闭。不知道还有没有更好的方法来实现。
  • 相关阅读:
    CocoaPods使用详细说明
    cocoapod使用
    Android-利用LinearGradient实现文字一闪一闪
    Android5.0 CheckBox颜色修改
    android实现文字渐变效果和歌词进度的效果
    Ceph 常规操作笔记
    Git版本控制器使用总结性梳理
    CentOS 7.5 部署 MySQL 5.7 基于GTID主从复制+并行复制+半同步复制+读写分离(ProxySQL) 环境- 运维笔记 (完整版)
    Ansible-playbook 运维笔记
    Docker容器基础介绍
  • 原文地址:https://www.cnblogs.com/vishun/p/15527810.html
Copyright © 2011-2022 走看看