当前位置: 首页 > 科技观察

面试官:你知道Dubbo是如何优雅的上线下线的吗?你:什么是线上和线下的优雅?

时间:2023-03-22 11:02:43 科技观察

最近校招和社招都在如火如荼的进行着。我也承担了很多面试工作。在一次面试中,我和应聘者聊起了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{@OverridepublicvoidonApplicationEvent(ContextClosedEventevent){//做容器关闭前的清理工作}}Dubbo优雅的离线离线因为Spring提供了ApplicationListener接口来帮助我们监听容器关闭事件,所以很多web容器、框架等都可以通过这种机制来做自己优雅的在线离线操作。比如tomcat、dubbo等都是这样做的。这里简单回答一下Dubbo。在Dubbo官网上,有关于优雅关闭的介绍:应用在关闭时,收到关闭通知后,会先将自己标记为不接受(发起)新的请求,然后等待10s。(默认10秒),等待执行线程执行完毕。那么,他之所以能做这些事情,是因为操作系统,到JVM,到Spring等,对优雅关机的支持做得很好。关于各个版本的Dubbo如何使用JVM的shutdownhook机制,或者说Spring的事件机制的优雅关闭,我同事的一篇文章介绍的很清楚。您可以在这里阅读:https://www.cnkirito.moe/dubbo-gracefully-shutdown/介绍了Dubbo2.5到Dubbo2.7的历史版本,Dubbo遇到的问题和解决方案,以解决优雅下线的问题。目前在Dubbo中的实现方法如下,同样使用了Spring的事件机制:dbo().unregister();}BeanFactoryUtils.addApplicationListener(context,SHUTDOWN_HOOK_LISTENER);}}小结本文从操作系统入手,介绍了Linux、Docker、JVM、Spring、Dubbo的优雅关机支持。可见,一个简单的优雅关机功能,需要如此多的底层基础设施和上下游上层应用的支持。相信通过本文的学习,你一定对优雅线上线下有了更深入的了解。另外,也希望大家在通过本文之后,在遇到一些实际问题的时候,能够想到文中提到的shutdownhook机制和Spring的事件机制。在很多情况下,这些机制可以帮助我们解决很多问题。在我的工作中,我多次使用过这样的机制,以后有机会再给大家介绍几个例子。好了,这就是本文的全部内容。如果对你有帮助,记得点三连哦~参考:https://zhuanlan.zhihu.com/p/29093407https://www.cnkirito.moe/dubbo-gracefully-shutdown/