如何优雅的停止spring boot service

前言

往往"停止服务"的代名词就是暴力,不计后果的,因为在强制停止的时候,不会管里面是否还有正在运行的线程。

碰巧最近由于在搞AWS的auto scalinng,不知道的朋友,可以把它理解为AWS可以自动的扩展或者是收缩我们的服务器,使得可以减少经费,想更深入了解的可以自行google。

这个出发点好是好,但是我也在实际使用的时候,发现了点问题:如果docker被stop了,里面可能存活的就被强制停止了,这个时候我么应该怎么办呢?

正文

根据

  1. docker stop命令实际上执行的是kill pid 指令,如果不跟随停止信号的话,默认情况下使用的是SIGNTEMR
  2. 并且如果docker中的主进程被停止,那么docker自然会停止。

所以推断问题的关键在于,我们需要去操控spring boot 需要优雅的stop,也就是我们今天的主角。

说了这么多废话该提起,下面进入正题,网上其实有很多这方面的教程例如说下面这个就写的很好:

https://www.cnblogs.com/harrychinese/p/SpringBoot-graceful-shutdown.html

但是网上的文档几乎都是把注入bean放在启动类中的,而我给它放在了@configuration 的类里,下面呢看下主要代码:

首先是最主要的监听容器关闭,并且进行处理的代码:

package com.demo.timeout.tomcat;

import org.apache.catalina.connector.Connector;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.stereotype.Component;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Component
public class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
    private volatile Connector connector;
    private int waitTime;

    @Value("${STOP_WAIT_TIMEOUT}")
    public void setWaitTime(int waitTime) {
        this.waitTime = waitTime;
    }
    @Override
    public void customize(Connector connector) {
        this.connector = connector;
    }

    @Override
    public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
        this.connector.pause();
        Executor executor = this.connector.getProtocolHandler().getExecutor();
        try {
            if (executor instanceof ThreadPoolExecutor) {
                ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
                threadPoolExecutor.shutdown();
                if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) {
                    System.out.println("Tomcat 进程在" + waitTime + " 秒内无法结束,尝试强制结束");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            Thread.currentThread().interrupt();
        }
    }
}

然后是将其注入的代码:

package com.demo.timeout.config;

import com.demo.timeout.tomcat.GracefulShutdown;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class GracefulShutDownConfig {

    @Autowired
    private GracefulShutdown gracefulShutdown;

    @Bean
    public ServletWebServerFactory servletContainer() {
        TomcatServletWebServerFactory tomcatServletWebServerFactory = new TomcatServletWebServerFactory();
        tomcatServletWebServerFactory.addConnectorCustomizers(gracefulShutdown);
        return tomcatServletWebServerFactory;
    }
}

最后写了一个API的测试代码

package com.demo.timeout.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @GetMapping("/30s-process")
    public String longProcessAPI(){
        System.out.println("enter this long process function");
        try {
            Thread.sleep(30000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "30s线程启动";
    }
}

如果有需要可以看一下我上传的代码:

https://github.com/luckypoison/SpringBoot-Shutdown-Graceful

另外说一下如果用的不是spring boot内嵌的tomcat,那么我们只能通过改tomcat的配置。
具体的改法是我们应该修改conf文件下的context.xml文件,加上一个“unloadDelay”属性,这个属性的值为超时时间的值,如果在这个时间之内运行完了,则tomcat关闭,否则tomcat将强制关闭,代码案例如下:

<?xml version="1.0" encoding="UTF-8"?>
<!--
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-->
<!-- The contents of this file will be loaded for each web application -->
<Context unloadDelay="60000">

    <!-- Default set of monitored resources. If one of these changes, the    -->
    <!-- web application will be reloaded.                                   -->
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
    <WatchedResource>WEB-INF/tomcat-web.xml</WatchedResource>
    <WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>

    <!-- Uncomment this to disable session persistence across Tomcat restarts -->
    <!--
    <Manager pathname="" />
    -->
</Context>
上一篇:Angular4总结(七)—— 与服务器通信


下一篇:脚本语言适合物联网开发吗