一、服务下线方案

(一)粗鲁下线

粗鲁下线应该绝对禁止

kill -n <pid> # n为信号变量
例:
kill -9 <pid> # 强制停止
kill -15 <pid> # 如果程序正在IO,可能不会立刻做出反映

在停止的那一霎那,应用中正在处理的业务逻辑会被中断,导致产生业务异常情形。

# 查看linux常见的信号变量
$ kill -l

参考:Linux kill -9 和 kill -15 的区别

springBoot如何主动让用户退出登录 session spring优雅退出_java

普通服务优雅下线

(1)web容器优雅停止

在 Spring Boot 2.3 中增加了新特性优雅停止。下面都支持优雅停止
(1)Spring Boot 内置的四钟嵌入式 Web 服务器(Jetty、Reactor Netty、Tomcat 和 Undertow)
(2)反应式框架
(3)基于 Servlet 的 Web 应用程序

大概逻辑就是先停止外部的所有新请求,然后再处理关闭前收到的请求

springBoot如何主动让用户退出登录 session spring优雅退出_spring_02

application.yml 中添加一些配置来启用优雅停止

# 开启优雅停止 Web 容器,默认为 IMMEDIATE:立即停止
server:
  shutdown: graceful

# 最大等待时间
spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s

(2)SpringApplication.exit(context);

(3)acturator 的 shutdown 端点

自定义EndPoint - gitee.com

原理:执行 doClose() 方法,关闭并销毁 applicationContext

(4)自定义 ApplicationListener

Nacos 服务下线

(1) 创建脚本

k8s 容器生命周期中的prestop阶段配置一个执行脚本:

lifecycle:
            preStop:
              exec:
                command:
                  - /bin/sh
                  - '-c'
                  - 'curl -X POST  ''http://localhost:8080/actuator/stopService'';\'   # 
                  - sleep 30;

(2) 暴露出关闭接口

/** 使用了nacos注册中心的服务关闭端点配置 */
@ConditionalOnClass(NacosAutoServiceRegistration.class)
@RestController
@RequestMapping("actuator")
@RequiredArgsConstructor
@Slf4j
public class NacosStopEndpoint {
    private final NacosAutoServiceRegistration nacosAutoServiceRegistration;
    private final ApplicationContext context;

    /** 注销服务后关闭应用前等待的时间(毫秒) */
    @Value("${stopService.waitTime:10000}")
    private int waitTime;

    /**
     * 关闭服务 <br>
     * 只接收localhost发起的请求
     *
     * @param request
     * @return
     */
    @PostMapping("stopService")
    public ResponseEntity<Boolean> stopNacosService(HttpServletRequest request) {
        //只接受本机请求 
        if (!request.getServerName().equalsIgnoreCase("localhost")){
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(false);
        }
		//开启异步线程:先从Nacos注销,等待1000毫秒后,关闭容器
        new Thread(
                        () -> {
                            log.info("Ready to stop service");
                            nacosAutoServiceRegistration.stop(); // 一、从Nacos注销
                            log.info("Nacos instance has been de-registered");
                            log.info("Waiting {} milliseconds...", waitTime);
                            try {
                                Thread.sleep(waitTime); // 二、等待1000毫秒后
                            } catch (InterruptedException e) {
                                log.info("interrupted!", e);
                            }
                            log.info("Closing application...");
                            SpringApplication.exit(context); // 三、关闭容器
                            ((ConfigurableApplicationContext) context).close();
                        })
                .start();

        return ResponseEntity.ok(true);
    }
}

二、服务注销 原理

(一)Eureka 注销服务

Eureka Server 存在三个变量:(registry、readWriteCacheMap、readOnlyCacheMap) 保存服务注册信息

ReadWriteMap 读写缓存
ReadOnlyMap 只读缓存

springBoot如何主动让用户退出登录 session spring优雅退出_Time_03

定时任务
(1)定时任务每 30s 将 readWriteCacheMap 同步至 readOnlyCacheMap
(2)每 60s 清理超过 90s 未续约的节点
(3)Eureka Client 每 30s 从 readOnlyCacheMap 更新服务注册信息

springBoot如何主动让用户退出登录 session spring优雅退出_Web_04

(二)Nacos 注销服务-待续

nacos收到注销请求,如何注销服务?