最近校招和社招都在如火如荼的进行着。我也承担了很多面试工作。在一次面试中,我和应聘者聊起了Dubbo。Dubbo是一个比较知名的RPC框架。很多人对它的一些网络通信、通信协议、动态代理等都有一定的了解,这个候选人也是一样。不过接下来我问了他一个问题:你在使用Dubbo的时候,如果应用重启了,你怎么保证一个请求不会中断?他什么也没说,我以为他没听懂我的问题,我就问他:我只想问一下Dubbo是如何优雅地做线上线下的,你知道吗?然后他问我:什么叫线上线下优雅??好的。在这篇文章中,我来介绍一下这个知识点。优雅的登录和退出关于“优雅的登录和退出”这个词,我找不到官方的解释,所以我试着解释一下它是什么。首先,大家必须非常清楚线上线下。例如,在应用发布过程中,我们需要先停止应用服务,然后再启动服务。这个过程包括离线和在线。那么,如何理解“优雅”呢?先说一下我们认为不优雅的地方:1.服务停止时,没有关闭相应的监控,导致应用停止后出现大量告警。2、当应用停止时,没有通知外部调用者,就会有很多请求过来,导致很多调用失败。3、当应用程序停止时,有一些线程正在执行,执行到一半,JVM进程就被杀死了。4、应用启动时,服务还没有准备好,就开始对外提供服务,导致调用失败多次。5、应用启动时,没有检查应用的健康状态就开始对外提供服务,导致调用失败多次。以上都是我们认为不雅的情况。那么反过来,优雅注销下线是避免上述情况的一种手段。一个应用的优雅下线其实涉及的东西很多,从底层操作系统、容器层面,到编程语言、框架层面,再到应用架构层面,涉及的知识非常广泛。其实优雅注销最重要的就是优雅注销。因为如果下线过程不优雅,会出现调用失败、服务失败等很多问题。所以很多时候,人们也会提到优雅停机的概念。本文后面介绍的优雅注销和注销也着重介绍了优雅关机的过程。操作系统&容器的优雅离线离线运行关于操作系统,我之前在一篇文章中介绍过这个话题。你可能没有注意到,当时介绍的话题是为什么不能直接在在线机器上执行kill-9。其实这背后的思路就是优雅下线。我们知道之所以不推荐使用kill-9,是因为kill-9特别强,系统会发出SIGKILL信号,要求接收到信号的程序立即结束运行,不能阻塞或忽略。这个过程显然是不优雅的,因为如果立即停止应用程序,就没有办法执行收尾动作。而更优雅的方式是kill-15。使用kill-15时,系统会向相应的程序发送一个SIGTERM信号。当程序收到信号后,如何处理就看自己了。kill-15会通知应用程序,这是操作系统对优雅注销和下线最基本的支持。过去,在操作系统之上有应用程序。但是自从引入容器化技术后,在操作系统和应用之间多了一层容器层,而Docker、k8s等容器其实是支持优雅离线和离线的。比如Docker也提供了dockerstop和dockerkill两个命令。就像kill-15一样,dockerstop会向容器中的进程发送一个SIGTERM信号,10S后(可以通过参数指定)再发送一个SIGKILL信号。而dockerkill就像kill-9一样,直接发送SIGKILL信号。在操作系统和容器基本支持JVM的优雅注销和注销后,在收到dockerstop、kill-15等命令后,会通知应用进程关闭进程。Java应用程序在运行时,是一个独立运行的进程。这个过程如何结束?Java程序的终止是基于JVM的关闭。JVM的关机方式分为正常关机、强制关机和非正常关机三种。其中,正常关机是为了支持优雅注销和离线。在正常关闭期间,JVM可以执行一些清理操作,例如删除临时文件。当然,开发者也可以自定义做一些额外的事情,比如通知应用框架进行优雅的线上线下操作。而这个机制是通过JDK中提供的shutdownhook来实现的。JDK提供了Java.Runtime.addShutdownHook(Threadhook)方法,可以注册一个JVM关闭的钩子。示例如下:packagecom.hollis;publicclassShutdownHookTest{publicstaticvoidmain(String[]args){booleanflag=true;Runtime.getRuntime().addShutdownHook(newThread(()->{System.out.println("hookexecute...");}));while(flag){//appisruning}System.out.println("mainthreadexecuteend...");}}执行命令:jps6520ShutdownHookTest6521Jpskill6520控制台输出:hookexecute...Processfinishedwithexitcode143(interruptedbysignal15:SIGTERM)可以看到现在,当我们使用kill(默认kill-15)关闭进程,程序会先执行我注册的shutdownHook,然后退出,会提示:interruptedbysignal15:SIGTERMSpring优雅的在线离线JVM提供shutdownhook后,很多框架都可以通过这种机制来支持优雅离线。比如Spring,他会向JVM注册一个shutdownhook,在收到shutdown通知的时候,会销毁bean,销毁容器等操作。同时,Spring作为一个成熟的框架,还提供了事件机制,可以用来实现更加优雅的线上线下功能。ApplicationListener是Spring事件机制的一部分,它配合抽象类ApplicationEvent完成ApplicationContext事件机制。开发者可以实现ApplicationListener接口,监听Spring容器的关闭事件(ContextClosedEvent),做一些特殊处理:@ComponentpublicclassMyListenerimplementsApplicationListener
