美文网首页
java中如何优雅的停止服务

java中如何优雅的停止服务

作者: 不怕天黑_0819 | 来源:发表于2020-08-30 10:46 被阅读0次

    强制进程退出的副作用:

    • 缓存中的数据尚未持久化到磁盘中,导致数据丢失;
    • 正在进行文件的write操作,没有更新完成,突然退出,导致文件损坏;
    • 线程池的任务队列中尚有接收到的任务还没来得及处理,导致任务丢失;
    • 数据库操作已经完成,例如账户余额更新,准备返回应答消息给客户端时,消息尚在通信线程的发送队列中排队等待发送,进程强制退出导致应答消息没有返回给客户端,客户端发起超时重试,会带来重复更新问题;
    • 其它问题等…

    解决方法:

    • 使用ShutdownHook 钩子程序来完成善后工作
    • 使用基于信号的进程通知机制

    针对线程池的停机:
    使用 DisposableBean 接口

    public abstract class ExecutorConfigurationSupport extends CustomizableThreadFactory
          implements DisposableBean{
        @Override
        public void destroy() {
            shutdown();
        }
    
        /**
         * Perform a shutdown on the underlying ExecutorService.
         * @see java.util.concurrent.ExecutorService#shutdown()
         * @see java.util.concurrent.ExecutorService#shutdownNow()
         * @see #awaitTerminationIfNecessary()
         */
        public void shutdown() {
            if (this.waitForTasksToCompleteOnShutdown) {
                this.executor.shutdown();
            }
            else {
                this.executor.shutdownNow();
            }
            awaitTerminationIfNecessary();
        }
    
        /**
         * Wait for the executor to terminate, according to the value of the
         * {@link #setAwaitTerminationSeconds "awaitTerminationSeconds"} property.
         */
        private void awaitTerminationIfNecessary() {
            if (this.awaitTerminationSeconds > 0) {
                try {
                    this.executor.awaitTermination(this.awaitTerminationSeconds, TimeUnit.SECONDS));
                }
                catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }
    

    1 通过 waitForTasksToCompleteOnShutdown 标志来控制是想立刻终止所有任务,还是等待任务执行完成后退出。

    2 executor.awaitTermination(this.awaitTerminationSeconds, TimeUnit.SECONDS)); 控制等待的时间,防止任务无限期的运行(前面已经强调过了,即使是 shutdownNow 也不能保证线程一定停止运行)。

    更多需要我们的思考的优雅停机策略

    服务治理框架一般会考虑到优雅停机的问题。通常的做法是事先隔断流量,接着关闭应用。常见的做法是将服务节点从注册中心摘除,订阅者接收通知,移除节点,从而优雅停机;涉及到数据库操作,则可以使用事务的 ACID 特性来保证即使 crash 停机也能保证不出现异常数据,正常下线则更不用说了;又比如消息队列可以依靠 ACK 机制 + 消息持久化,或者是事务消息保障;定时任务较多的服务,处理下线则特别需要注意优雅停机的问题,因为这是一个长时间运行的服务,比其他情况更容易受停机问题的影响,可以使用幂等和标志位的方式来设计定时任务…

    事务和 ACK 这类特性的支持,即使是宕机,停电,kill -9 pid 等情况,也可以使服务尽量可靠;而同样需要我们思考的还有 kill -15 pid,正常下线等情况下的停机策略。最后再补充下整理这个问题时,自己对 jvm shutdown hook 的一些理解。

    shutdown hook 会保证 JVM 一直运行,直到 hook 终止 (terminated)。这也启示我们,如果接收到 kill -15 pid 命令时,执行阻塞操作,可以做到等待任务执行完成之后再关闭 JVM。同时,也解释了一些应用执行 kill -15 pid 无法退出的问题,没错,中断被阻塞了。

    注:若需要平滑停止服务,我们一般可以通过ShutdownHook和Signal来实现。ShutdownHook一般比较难保证关闭任务的执行顺序,这个时候可以考虑使用Signal机制来完全托管我们关闭服务的执行顺序。

    参考链接:
    https://www.cnkirito.moe/gracefully-shutdown/

    https://www.cnblogs.com/panchanggui/p/9806266.html

    https://juejin.im/post/6844903814181421064

    相关文章

      网友评论

          本文标题:java中如何优雅的停止服务

          本文链接:https://www.haomeiwen.com/subject/bfossktx.html