如何优雅的停止spring boot service

简介: 前言 往往"停止服务"的代名词就是暴力,不计后果的,因为在强制停止的时候,不会管里面是否还有正在运行的线程。 碰巧最近由于在搞AWS的auto scalinng,不知道的朋友,可以把它理解为AWS可以自动的扩展或者是收缩我们的服务器,使得可以减少经费,想更深入了解的可以自行google。

前言

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

碰巧最近由于在搞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>
目录
相关文章
|
4月前
|
前端开发 Java 开发者
深入理解Spring Boot中的@Service注解
【4月更文挑战第22天】在 Spring Boot 应用开发中,@Service 注解扮演着特定的角色,主要用于标识服务层组件。本篇技术博客将全面探讨 @Service 注解的概念,并提供实际的应用示例,帮助开发者理解如何有效地使用这一注解来优化应用的服务层架构
1123 1
|
4月前
SpringBoot+Mybatis-Plus+PageHelper分页+多条件查询
SpringBoot+Mybatis-Plus+PageHelper分页+多条件查询
105 0
|
4月前
|
Java 测试技术
SpringBoot整合单元测试&&关于SpringBoot单元测试找不到Mapper和Service报java.lang.NullPointerException的错误
SpringBoot整合单元测试&&关于SpringBoot单元测试找不到Mapper和Service报java.lang.NullPointerException的错误
94 0
|
28天前
|
Java Windows
SpringBoot Windows 自启动 - 通过 Windows Service 服务实现
SpringBoot Windows 自启动 - 通过 Windows Service 服务实现
32 2
|
18天前
|
Java Spring
【Azure Service Bus】使用Spring Cloud integration示例代码,为多个 Service Bus的连接使用 ConnectionString 方式
【Azure Service Bus】使用Spring Cloud integration示例代码,为多个 Service Bus的连接使用 ConnectionString 方式
|
18天前
|
Java Spring
【Azure 服务总线】Spring Cloud 的应用 使用Service Bus 引起 org.springframework.beans.BeanInstantiationException 异常,无法启动
【Azure 服务总线】Spring Cloud 的应用 使用Service Bus 引起 org.springframework.beans.BeanInstantiationException 异常,无法启动
|
18天前
|
Java 开发工具 Spring
【Azure Spring Cloud】使用azure-spring-boot-starter-storage来上传文件报错: java.net.UnknownHostException: xxxxxxxx.blob.core.windows.net: Name or service not known
【Azure Spring Cloud】使用azure-spring-boot-starter-storage来上传文件报错: java.net.UnknownHostException: xxxxxxxx.blob.core.windows.net: Name or service not known
|
18天前
|
前端开发 JavaScript Java
【Azure 应用服务】App Service For Windows 中如何设置代理实现前端静态文件和后端Java Spring Boot Jar包
【Azure 应用服务】App Service For Windows 中如何设置代理实现前端静态文件和后端Java Spring Boot Jar包
|
18天前
|
Java Linux C++
【Azure 应用服务】App Service For Linux 部署Java Spring Boot应用后,查看日志文件时的疑惑
【Azure 应用服务】App Service For Linux 部署Java Spring Boot应用后,查看日志文件时的疑惑
|
3月前
|
运维 Java 关系型数据库
Spring运维之boot项目bean属性的绑定读取与校验
Spring运维之boot项目bean属性的绑定读取与校验
42 2