SpringBoot 实战 之 优雅终止服务的方法

时间:2022-06-20 05:37:33

由于 springboot 是一个微服务框架,其生产部署的方式也需要尽可能的简单,与常规的 web 应用有着一个巨大的不同之处,它可以内嵌一个 web 容器,如:tomcat、jetty等,不再需要将应用打包成容器规定的特定形式。

对于 springboot 来说,打包成一个简单的 jar 包直接使用 java -jar即可启动,这是一种非常优雅的方式,但同时也带来了一定的问题,如:应用如何停止?在过去,应用程序是部署在特定的容器中的,使用容器提供的脚本可以优雅停服,但现在容器被内嵌了,脚本没有了,怎么办?直接 kill 是一种方式,但未免显得太过粗鲁,而且可能带来许多意想不到的问题。

既然我们能想到问题,框架的制定者也会想到,那么他们有没有为我们准备好解决方案呢?答案是有的,下面我介绍下我了解到的几种方案。

1. 使用 endpoints

在 springboot 官方文档的第4部分中介绍了为应用发布生产准备的各种特性,其中,通过 actuator 的 http endpoint,开发人员可以方便地对应用的监控与管理。

引入指定的 starter 包:

?
1
"org.springframework.boot:spring-boot-starter-actuator:${springbootversion}"

在 application.yml 中打开如下两个配置,即可实现通过 http 请求停止应用

?
1
2
3
4
5
6
management:
 security:
  enabled: false
endpoints:
 shutdown:
  enabled: true

操作命令如下:

?
1
curl -x post http://host:port/shutdown

但这种方式有一个非常严重的问题,那就是任意人都可以控制应用的停止,这对于一个生产应用无疑是不可接受的。有些人可能会想,现在的链接地址太简单了,非维护人员也可以轻易地猜出来,如果使用一个非常复杂的地址是否可以避免这个问题。很好,这个提议不错,那我们再看看 springboot 为我们提供的相关配置。

?
1
2
3
4
endpoints:
 shutdown:
  enabled: true
  path: /xxx

配置完成后,上面的命令就不可用了,需更新命令为:

?
1
curl -x post http://host:port/xxx

其中的/xxx当然只是我随手设置的一个,你可以设置任意的地址。虽然安全性高了那么一点,但这样的安全级别仍然是无法应用到生产环境的。那是否还有其它的防护手段呢?有,除了修改shutdown的路径外,我们还可以给所有的管理操作加上一个统一的上下文,配置独立的端口,并限制指定ip访问(一般限定为本机),配置如下:

?
1
2
3
4
5
6
management:
 security:
  enabled: false
 port: 9001
 address: 127.0.0.1
 context-path: /admin

变更后的停服命令为:

?
1
curl -x post http://127.0.0.1:9001/admin/xxx

这样其实已经足够安全了,为了进一步的保证系统的安全,再给其加上一层 http basic auth。

增加 security 依赖:

?
1
"org.springframework.boot:spring-boot-starter-security:${springbootversion}"

修改配置文件如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
endpoints:
 shutdown:
  enabled: true
  path: /xxx
management:
 security:
  enabled: true
 port: 9001
 address: 127.0.0.1
 context-path: /admin
security:
 basic:
  enabled: true
  path: /admin
 user:
  name: root
  password: 123456

配置完成后,最终的停服命令为:

?
1
curl -x post -u root:123456 http://127.0.0.1:9001/admin/xxx

2. 注册为系统服务

除了使用 java -jar 运行 springboot 应用程序外,还可以轻松地用 init.d 或 systemd 注册成 linux/unix 系统服务,这使得在生产环境中,安装和管理 springboot 应用程序变得非常简单。

在maven工程里面,为了创建一个“完全可执行”的 jar,需要引入如下插件:

?
1
2
3
4
5
6
7
8
<plugin>
  <groupid>org.springframework.boot</groupid>
  <artifactid>spring-boot-maven-plugin</artifactid>
  <version>1.5.2.release</version>
  <configuration>
    <executable>true</executable>
  </configuration>
</plugin>

在 gradle 工程里面,等效配置如下:

?
1
2
3
4
5
6
7
plugins {
  id 'org.springframework.boot' version '1.5.2.release'
}
 
springboot {
  executable = true
}

配置完成后,即可以通过 ./application-name.jar 运行构建好的应用程序。

最后,我们需要将打包好的应用程序安装成一个init.d 服务,这样就可以很方便地使用 unix/linux 进行管理了。操作方式很简单,只需要将应用程序简单的链接到 init.d 即可(其中funda为我自己的应用名,自己实验时需要视情况替换)。

?
1
ln -s /app/funda/funda.jar /etc/init.d/funda

检查链接是否建立成功

?
1
ls -l /etc/init.d/funda

启动服务,应用日志可查看文件 /var/log/funda.log

?
1
service funda start

其它常用命令

?
1
2
3
4
# 查看应用运行状态
service funda status
# 停止应用
service funda stop

问题汇总:

在链接成功后,应用启动时,无法成功启动,提示unable to find java,使用如下命令将jdk的java命令链接到/sbin/java即可。

?
1
ln -s /usr/local/jdk1.8.0_131/bin/java /sbin/java

参考链接:

endpoints
monitoring and management over http
unix/linux services
unable to find java #5690

项目链接:

github : https://github.com/qchery/funda

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

原文链接:https://blog.csdn.net/chinrui/article/details/78685032