详解Springboot 优雅停止服务的几种方法

在使用Springboot的时候,都要涉及到服务的停止和启动,当我们停止服务的时候,很多时候大家都是kill -9 直接把程序进程杀掉,这样程序不会执行优雅的关闭。而且一些没有执行完的程序就会直接退出。

我们很多时候都需要安全的将服务停止,也就是把没有处理完的工作继续处理完成。比如停止一些依赖的服务,输出一些日志,发一些信号给其他的应用系统,这个在保证系统的高可用是非常有必要的。那么咱么就来看一下几种停止springboot的方法。

第一种就是Springboot提供的actuator的功能,它可以执行shutdown, health, info等,默认情况下,actuator的shutdown是disable的,我们需要打开它。首先引入acturator的maven依赖。

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
  </dependency>

然后将shutdown节点打开,也将/actuator/shutdown暴露web访问也设置上,除了shutdown之外还有health, info的web访问都打开的话将management.endpoints.web.exposure.include=*就可以。将如下配置设置到application.properties里边。设置一下服务的端口号为3333。

server.port=3333management.endpoint.shutdown.enabled=true
management.endpoints.web.exposure.include=shutdown

接下来,咱们创建一个springboot工程,然后设置一个bean对象,配置上PreDestroy方法。这样在停止的时候会打印语句。bean的整个生命周期分为创建、初始化、销毁,当最后关闭的时候会执行销毁操作。在销毁的方法中执行一条输出日志。

package com.hqs.springboot.shutdowndemo.bean;

import javax.annotation.PreDestroy;

/**
 * @author huangqingshi
 * @Date 2019-08-17
 */
public class TerminateBean {

  @PreDestroy
  public void preDestroy() {
    System.out.println("TerminalBean is destroyed");
  }

}

做一个configuration,然后提供一个获取bean的方法,这样该bean对象会被初始化。

package com.hqs.springboot.shutdowndemo.config;

import com.hqs.springboot.shutdowndemo.bean.TerminateBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author huangqingshi
 * @Date 2019-08-17
 */
@Configuration
public class ShutDownConfig {

  @Bean
  public TerminateBean getTerminateBean() {
    return new TerminateBean();
  }

}

在启动类里边输出一个启动日志,当工程启动的时候,会看到启动的输出,接下来咱们执行停止命令。

curl -X POST http://localhost:3333/actuator/shutdown

以下日志可以输出启动时的日志打印和停止时的日志打印,同时程序已经停止。是不是比较神奇。

第二种方法也比较简单,获取程序启动时候的context,然后关闭主程序启动时的context。这样程序在关闭的时候也会调用PreDestroy注解。如下方法在程序启动十秒后进行关闭。

    /* method 2: use ctx.close to shutdown all application context */
    ConfigurableApplicationContext ctx = SpringApplication.run(ShutdowndemoApplication.class, args);

    try {
      TimeUnit.SECONDS.sleep(10);

    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    ctx.close();

第三种方法,在springboot启动的时候将进程号写入一个app.pid文件,生成的路径是可以指定的,可以通过命令 cat /Users/huangqingshi/app.id | xargs kill 命令直接停止服务,这个时候bean对象的PreDestroy方法也会调用的。这种方法大家使用的比较普遍。写一个start.sh用于启动springboot程序,然后写一个停止程序将服务停止。  

/* method 3 : generate a pid in a specified path, while use command to shutdown pid :
      'cat /Users/huangqingshi/app.pid | xargs kill' */
    SpringApplication application = new SpringApplication(ShutdowndemoApplication.class);
    application.addListeners(new ApplicationPidFileWriter("/Users/huangqingshi/app.pid"));
    application.run();

第四种方法,通过调用一个SpringApplication.exit()方法也可以退出程序,同时将生成一个退出码,这个退出码可以传递给所有的context。这个就是一个JVM的钩子,通过调用这个方法的话会把所有PreDestroy的方法执行并停止,并且传递给具体的退出码给所有Context。通过调用System.exit(exitCode)可以将这个错误码也传给JVM。程序执行完后最后会输出:Process finished with exit code 0,给JVM一个SIGNAL。

 /* method 4: exit this application using static method */
    ConfigurableApplicationContext ctx = SpringApplication.run(ShutdowndemoApplication.class, args);
    exitApplication(ctx);
  public static void exitApplication(ConfigurableApplicationContext context) {
    int exitCode = SpringApplication.exit(context, (ExitCodeGenerator) () -> 0);

    System.exit(exitCode);
  }

第五种方法,自己写一个Controller,然后将自己写好的Controller获取到程序的context,然后调用自己配置的Controller方法退出程序。通过调用自己写的/shutDownContext方法关闭程序:curl -X POST http://localhost:3333/shutDownContext。

package com.hqs.springboot.shutdowndemo.controller;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author huangqingshi
 * @Date 2019-08-17
 */
@RestController
public class ShutDownController implements ApplicationContextAware {

  private ApplicationContext context;

  @PostMapping("/shutDownContext")
  public String shutDownContext() {
    ConfigurableApplicationContext ctx = (ConfigurableApplicationContext) context;
    ctx.close();
    return "context is shutdown";
  }

  @GetMapping("/")
  public String getIndex() {
    return "OK";
  }

  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    context = applicationContext;
  }
}

好了,springboot的优雅关闭方法也都实现好了,也有同学问,如何暴力停止呢,简单,直接kill -9 相应的PID即可。

总结一下:

以上这几种方法实现的话比较简单,但是真实工作中还需要考虑的点还很多,比如需要保护暴露的点不被别人利用,一般要加一些防火墙,或者只在内网使用,保证程序安全。

在真实的工作中的时候第三种比较常用,程序中一般使用内存队列或线程池的时候最好要优雅的关机,将内存队列没有处理的保存起来或线程池中没处理完的程序处理完。但是因为停机的时候比较快,所以停服务的时候最好不要处理大量的数据操作,这样会影响程序停止。

好了,大家觉得还没看全的话,可以访问我的GIT代码:https://github.com/stonehqs/shutdowndemo.git

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 详解Spring Boot应用的启动和停止(start启动)

    Spring Boot,作为Spring框架对"约定优先于配置(Convention Over Configuration)"理念的最佳实践的产物,它能帮助我们很快捷的创建出独立运行.产品级别的基于Spring框架的应用,大部分Spring Boot应用只需要非常少的配置就可以快速运行起来,是一个与微服务(MicroServices)相当契合的微框架. 下面主要有两种方式进行Spring Boot的关闭:通过HTTP发送shutdown信号,或者通过service stop的方式. 一

  • Centos下SpringBoot项目启动与停止脚本的方法

    使用Maven脚本在Centos服务器中启动与停止项目 首先项目需要引用Maven的SpringBoot插件 <profiles> <profile> <id>boot-repackage</id> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-

  • Linux 启动停止SpringBoot jar 程序部署Shell 脚本的方法

    废话不多说了,先给大家上代码,具体代码如下所示: #!/bin/bash cd `dirname $0` CUR_SHELL_DIR=`pwd` CUR_SHELL_NAME=`basename ${BASH_SOURCE}` #修改这里jar包名即可 JAR_NAME="xxxxxxxxxxxx.jar" JAR_PATH=$CUR_SHELL_DIR/$JAR_NAME #JAVA_MEM_OPTS=" -server -Xms1024m -Xmx1024m -XX:Pe

  • Linux编辑启动、停止与重启springboot jar包脚本实例

    前言 springboot的配置文件中,配置文件的名字都有各自的意义跟用途 dev 开发环境 prod 生产环境(默认) test 测试环境 加载指定配置文件 --spring.profiles.active=prod springboot加载jar包的方式有 // 直接在控制台进行启动,缺点就是控制台关闭项目也就关闭了. java -jar bootdo.jar // 这种方式可以运行在后台,但是如果推出了shell的话,那也会挂 java -jar /bootdo-2.0.0.jar > b

  • 详解Springboot 优雅停止服务的几种方法

    在使用Springboot的时候,都要涉及到服务的停止和启动,当我们停止服务的时候,很多时候大家都是kill -9 直接把程序进程杀掉,这样程序不会执行优雅的关闭.而且一些没有执行完的程序就会直接退出. 我们很多时候都需要安全的将服务停止,也就是把没有处理完的工作继续处理完成.比如停止一些依赖的服务,输出一些日志,发一些信号给其他的应用系统,这个在保证系统的高可用是非常有必要的.那么咱么就来看一下几种停止springboot的方法. 第一种就是Springboot提供的actuator的功能,它

  • 详解Golang开启http服务的三种方式

    前言 都说go标准库实用,Api设计简洁.这次就用go 标准库中的net/http包实现一个简洁的http web服务器,包括三种版本. v1最简单版 直接使用http.HandleFunc(partern,function(http.ResponseWriter,*http.Request){}) HandleFunc接受两个参数,第一个为路由地址,第二个为处理方法. //v1 func main() { http.HandleFunc("/", func(w http.Respon

  • 详解SpringBoot配置文件启动时动态配置参数方法

    序言 当我们要同时启用多个项目而又要使用不同端口或者变换配置属性时,我们可以在配置文件中设置${变量名}的变量来获取启动时传入的参数,从而实现了动态配置参数,使启用项目更加灵活 例子 server: port: ${PORT:50101} #服务端口 spring: application: name: xc‐govern‐center #指定服务名 eureka: client: registerWithEureka: true #服务注册,是否将自己注册到Eureka服务中 fetchReg

  • 详解SpringBoot静态方法获取bean的三种方式

    目录 方式一  注解@PostConstruct 方式二  启动类ApplicationContext 方式三 手动注入ApplicationContext 方式一  注解@PostConstruct import com.example.javautilsproject.service.AutoMethodDemoService; import org.springframework.beans.factory.annotation.Autowired; import org.springfr

  • 详解Git建立本地仓库的两种方法

    Git是一种分布式版本控制系统,通常这类系统都可以与若干远端代码进行交互.Git项目具有三个主要部分:工作区,暂存目录,暂存区,本地目录: 安装完Git后,要做的第一件事,就是设置用户名和邮件地址.每个Git提交都使用此信息,并且将它永久地烘焙到您开始创建的提交中: $ git config --global user.name "John Doe" $ git config --global user.email johndoe@example.com 之后我们可以建立一个本地仓库.

  • 详解用Python处理Args的3种方法

    1. sys 模块 Python 中的 sys 模块具有 argv 功能.当通过终端触发 main.py 的执行时,此功能将返回提供给 main.py 的所有命令行参数的列表.除了其他参数之外,返回列表中的第一个元素是 main.py 的路径. 考虑下面的 main.py 示例 import sys list_of_arguments = sys.argv print(list_of_args[0]) print(list_of_args[1]) print(list_of_args[2]) p

  • 详解JavaScript中分解数字的三种方法

    本文基于免费代码营基本算法脚本"分解数字" 在数学中,非负整数n的阶乘可能是一个棘手的算法.在本文中,我将解释这种方法,首先使用递归函数,第二种使用而循环,第三种使用以循环. 算法挑战 返回提供的整体的阶乘. 如果整体用字母n表示,则阶乘是所有小于或等于n的正整数的乘积. 阶乘经常用简写符号n!表示! 例如:5!= 1 * 2 * 3 * 4 * 5 = 120 function factorialize(num) { return num; } factorialize(5); 提供

  • 详解Python实现图像分割增强的两种方法

    方法一 import random import numpy as np from PIL import Image, ImageOps, ImageFilter from skimage.filters import gaussian import torch import math import numbers import random class RandomVerticalFlip(object): def __call__(self, img): if random.random()

  • 详解Python实现字典合并的四种方法

    目录 1.用for循环把一个字典合并到另一个字典 2.用dict(b, **a)方法构造一个新字典 3.用b.update(a)的方法,更新字典 4.把字典转换成列表合并后,再转换成字典 (1)利用a.items().b.items()把a.b两个字典转换成元组键值对列表 (2)合并列表并且把合并后的列表转换成字典 5.实例,netmiko使用json格式的数据进行自动化操作 (1)json格式的处理 (2)json格式的设备信息列表 (3)netmiko读取json类型信息示例 1.用for循

  • 详解SpringBoot优雅编码之Lombok加持

    本文介绍了SpringBoot优雅编码之Lombok加持,分享给大家,具体如下: 概述 Lombok 通过提供简单的语法注解形式来帮助简化消除一些必须有但显得很臃肿的 java 代码.典型的是对于 POJO对象的简化(如自动帮我们生成Setter和Getter等),有了Lombok的加持,开发人员可以免去很多重复且臃肿的操作,极大地提高java代码的信噪比,因此我们必须尝试并应用起来! IntelliJ IDEA上配置 方法一:直接在IDEA界面中配置 首先进入Plugins界面: 然后搜索并安

随机推荐