Spring Boot提供了2种优雅关闭进程的方式:

  1. 基于管理端口关闭进程
  2. 基于系统服务方式关闭进程

基于管理端口关闭进程

基于管理端口方式实现进程关闭实际上是模块spring-boot-actuator提供的功能。

首先,需要在项目中添加对应模块依赖配置。

  • 添加Maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  • 添加Gradle依赖
dependencies {
compile("org.springframework.boot:spring-boot-starter-actuator")
}

其次,在配置文件中添加对应的参数配置(以文件application.properties为例说明)。

# 允许执行关闭操作
management.endpoint.shutdown.enabled=true
# 处于安全考虑,只允许在本地指定关闭操作
management.server.address=127.0.0.1
# 管理端口
management.server.port=8000
# 管理URL基础路径,默认为“/”
management.endpoints.web.base-path=/ops
# 配置关闭进程Endpoint
management.endpoints.web.path-mapping.shutdown=shutdown
# 对外暴露管理Endpoint
management.endpoints.web.exposure.include=info, health, shutdown

完成上述准备工作以后,启动Spring Boot应用,通过调用POST http://localhost:8000/ops/shutdown即可关闭进程。

实践中通常将上述关闭进程的URL调用写到脚本中,同时还可以结合别的方式一起确保进程一定能退出,如下为脚本示例(pname指进程名称):

#!/bin/bash
# 先通过管理端口关闭进程
curl -X POST http://127.0.0.1:8000/ops/shutdown --connect-timeout 3 --max-time 5 # 再次通过名称检查进程是否被成功停止
count=`ps -ef |grep pname |grep -v "grep" |wc -l`
if [ $count -gt 0 ]; then
if [ -f "$pid_file" ]; then
# 如果存在进程ID文件,则读取进程ID使用信号量通知方式关闭进程
pid=`cat $pid_file`
kill -15 $pid
else
# 通过名称方式查找到进程ID,使用信号量通知方式关闭进程
pid=`ps -ef |grep pname |grep -v "grep"| awk '{print $2}'`
kill -15 $pid
fi
fi

关于通过管理端口关闭Spring Boot进程的详细说明参见:https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#production-ready-endpoints

通过系统服务方式停止进程

Spring Boot支持直接将打包好的可执行jar包以系统服务方式运行,具体实现方式如下所述。

首先,将应用打包为完全可执行的jar包。

  • Maven打包配配置
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!-- 这个配置非常重要,使打包好的jar包具备可执行权限-->
<executable>true</executable>
</configuration>
</plugin>
  • Gradle打包配置
bootJar {
launchScript()
}

其次,将打包好的应用jar包添加为系统服务(在ubuntu18.04 LTS上实现,基于systemd)

1.假设将Spring Boot应用安装到/var/myapp目录下:将上述打包好的jar包拷贝到/var/myapp(目录不存在,手动创建)

2.在/etc/systemd/system下添加指定名称的系统服务:myapp.service,内容如下:

[Unit]
Description=myapp
After=syslog.target [Service]
User=root ## 注意:这里配置的是将来启动该服务的Linux系统用户名,影响权限
ExecStart=/var/myapp/myapp.jar
SuccessExitStatus=143 [Install]
WantedBy=multi-user.target

3.启动服务

$ sudo systemctl enable myapp.service
$ sudo systemctl start myapp.service

如果需要查看应用启动日志,请执行:$ journalctl -f

如果启动服务失败,请检查对应名称的服务文件是否放在正确位置(如:systemd系统需要放在/etc/systemd/system目录下),或者检查启动服务的用户权限,一些错误情形可以参考:https://springjavatricks.blogspot.com/2018/06/installing-spring-boot-services-in.html

关于将Spring Boot应用部署为系统服务的详细说明参见: https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#deployment-install

写在最后

我在如何优雅地停止Java进程中有讲到如何实现在进程退出之前做一些收尾的工作,这在Spring Boot中同样适用,只需要监听对应的信号量并注册JVM关闭钩子即可。

@SpringBootApplication
public class SpringbootApplication {
private static final Logger logger = LoggerFactory.getLogger(SpringbootApplication.class);
public static void main(String[] args) {
// 在Spring Boot应用中通过监听信号量和注册关闭钩子来实现在进程退出之前执行收尾工作
// 监听信号量
Signal sg = new Signal("TERM");
Signal.handle(sg, new SignalHandler() {
@Override
public void handle(Signal signal) {
logger.info("do signal handle: {}", signal.getName());
System.exit(0);
}
}); // 注册关闭钩子
Runtime.getRuntime().addShutdownHook(new Thread(){
@Override
public void run() {
// 执行收尾工作
logger.info("do something on shutdown hook");
}
}); SpringApplication.run(SpringbootApplication.class, args);
logger.info("Start DONE.");
}
}

另外,需要注意的是:在普通的Java应用程序中,当出现RuntimeExeception或OOM时会触发关闭钩子的执行;但是在Spring Boot应用中,当出现RuntimeException或OOM时并不会触发关闭钩子的执行(Spring Boot使用了嵌入式Tomcat)。

【参考】

https://www.jianshu.com/p/44ef43b282f0 正确、安全地停止SpringBoot应用服务

Spring Boot程序正确停止的姿势的更多相关文章

  1. 第一章 第一个spring boot程序(转载)

    第一章 第一个spring boot程序 本编博客转发自:http://www.cnblogs.com/java-zhao/p/5324185.html   环境: jdk:1.8.0_73 mave ...

  2. 我的第一个spring boot程序(spring boot 学习笔记之二)

    第一个spring boot程序 写在前面:鉴于spring注解以及springMVC的配置有大量细节和知识点,在学习理解之后,我们将直接进入spring boot的学习,在后续学习中用到注解及其他相 ...

  3. 构建Spring Boot程序有用的文章

    构建Spring Boot程序有用的文章: http://www.jb51.net/article/111546.htm

  4. Spring Boot从入门到精通(一)搭建第一个Spring Boot程序

    Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置.通过 ...

  5. Spring Boot程序接收命令行参数

    Spring Boot程序接收命令行参数 输入一行,回车,触发一次.如果想要调用service层,也是可以,能调用service层,就可以做很多事,触发一次就好比调用了一次http接口一样 packa ...

  6. Postman发送POST请求到Spring Boot的正确姿势

    最近用Spring Boot搭建了一些restful api,写起来真的很爽.但是当用Postman测试一些POST请求的接口的时候却遇到一些问题,上网冲浪查了一堆博客资料,发现都讲得不清不楚,于是记 ...

  7. spring boot 服务 正确关闭方式

    引言 Spring Boot,作为Spring框架对“约定优先于配置(Convention Over Configuration)”理念的最佳实践的产物,它能帮助我们很快捷的创建出独立运行.产品级别的 ...

  8. Spring Boot程序的执行流程

    Spring Boot的执行流程如下图所示:(图片来源于网络) 上图为SpringBoot启动结构图,我们发现启动流程主要分为三个部分,第一部分进行SpringApplication的初始化模块,配置 ...

  9. 第一个Spring Boot程序

    Windows 10家庭中文版,java version "1.8.0_152", Eclipse Oxygen.1a Release (4.7.1a),Spring Tools ...

随机推荐

  1. Caused by: java.lang.IllegalStateException: duplicate key: datasource

    java.lang.IllegalStateException: Failed to load property source from location 'classpath:/applicatio ...

  2. jsp request对象

    getParameter( ) :返回name指定参数的参数值 String[]  getParameterValues(String name)    :返回包含参数name的所有值的数值 getA ...

  3. keil中error: #70: incomplete type is not allowed—解决方法

    今天在写程序的时候,想使用sizeof求数组的大小,数组中其他c文件定义,在头文件使用extern uint8_t buff_value[]; 声明 但是keil编译报错,网上查了,发现,需要写成ex ...

  4. 无故出现 mysql dead but subsys locked的有关问题

    无故出现 mysql dead but subsys locked的问题问题描述:1.mysql安装完成后,使用service mysqld restart总是出现stop mysqld servic ...

  5. VGG梳理

    创新点(小卷积核.小池化核.层数更深.全连接变卷积) 对AlexNet改进,在第一个卷积层中使用了更小的卷积核和卷积stride 多尺度(训练和测试时,采用图片的不同尺度(当然是按各向同性缩放的最小边 ...

  6. Python-内存泄漏 持续增长 检查点

    仅个人目前遇见的内存问题, 可能不适用所有问题 一下只是简单的实例代码, 可能跑不起来, 只是看看 可变变量参数 小例子: def foo(a, b=[]): b.append(a) print b ...

  7. 洛谷 P1786 帮贡排序 题解

    P1786 帮贡排序 题目背景 在absi2011的帮派里,死号偏多.现在absi2011和帮主等人联合决定,要清除一些死号,加进一些新号,同时还要鼓励帮贡多的人,对帮派进行一番休整. 题目描述 目前 ...

  8. 洛谷2320 bzoj1192 鬼谷子的钱袋

    题目链接 题意概述:把正整数n分为m个正整数,m个正整数中不允许出现复数个非1的正整数,保证所有小于n的正整数都可以用一部分正整数的和表示,并且使m尽量小. 这道题不知道为啥bzoj上没有要求输出方案 ...

  9. Zookeeper——一致性协议:Zab协议

    Reference: https://www.jianshu.com/p/2bceacd60b8a 什么是Zab协议 Zab 协议的作用 Zab 协议原理 Zab 协议核心 Zab 协议内容 原子广播 ...

  10. while循环 运算符和编码

    昨日回顾 1. 初识python python是一门弱类型的解释型高级编程语言 解释器: CPython 官方提供的默认解释器. c语言实现的 PyPy 把python程序一次性进行编译. IPyth ...