本篇文章給大家分享的是有關(guān)如何排查Dubbo接口重復(fù)注銷問題,小編覺得挺實用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
成都創(chuàng)新互聯(lián)堅持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:成都網(wǎng)站設(shè)計、成都做網(wǎng)站、外貿(mào)網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時代的冊亨網(wǎng)站設(shè)計、移動媒體設(shè)計的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!
我在公司內(nèi)負(fù)責(zé)自研的dubbo注冊中心相關(guān)工作,群里經(jīng)常接到業(yè)務(wù)方反饋dubbo接口注銷報錯。經(jīng)排查,確定是同一個接口調(diào)用了兩次注銷接口導(dǎo)致,由于我們的注冊中心注銷接口不能重復(fù)調(diào)用,調(diào)用第二次會因為實例已經(jīng)注銷而報實例找不到的錯誤。
雖然這個報錯僅會打印一條錯誤日志,不影響業(yè)務(wù),但本著 follow through的精神,我決定還是一探究竟,更何況重復(fù)注銷也增加了應(yīng)用的結(jié)束時間,影響了發(fā)布回滾速度。
拿到業(yè)務(wù)方的dubbo版本,基于開源2.7.3內(nèi)部定制的一個版本,該版本修改主要涉及安全漏洞修復(fù)以及一些業(yè)務(wù)適配,寫了個demo跑起來,然后kill,發(fā)現(xiàn)果然報錯了。
為了確定不是內(nèi)部修改導(dǎo)致的問題,用開源的2.7.3版本再次測試,發(fā)現(xiàn)還是報錯。
同時為了確定這是一個bug,我將dubbo版本修改為2.7.7做測試,發(fā)現(xiàn)該版本不再報錯。
說明了重復(fù)注銷至少是開源dubbo 2.7.3的一個bug,在更高的2.7.7版本中已經(jīng)被修復(fù)。
于是有了解決方案:升級dubbo,但如果這么簡單就沒有這篇文章了。
內(nèi)部的dubbo已經(jīng)做了修改,想升級得把改動merge到新版本,比較費勁
就算升級了內(nèi)部的dubbo版本,也不可能這么快速推動業(yè)務(wù)方升級
所以應(yīng)該首先找到bug是哪里導(dǎo)致的,其次看注冊中心的擴展是否可以修復(fù)這個問題,如果不能修復(fù),就只能在內(nèi)部的dubbo版本中修復(fù)該問題。
懷疑ShutdownHook
由于這幾天研究過ShutdownHook(點擊查看原文跳轉(zhuǎn)《ShutdownHook原理》),第一時間懷疑ShutdownHook可能有問題。
dubbo 2.7.3代碼有關(guān)ShutdownHook的實現(xiàn)在DubboShutdownHook類,順著代碼梳理出如下關(guān)系
看到dubbo本身和spring都注冊了ShutdownHook,更加懷疑這里是不是ShutdownHook注冊重復(fù)了。于是debug看看是否是注冊重復(fù)了,這里給一個小經(jīng)驗,IntelliIDEA調(diào)試ShutdownHook執(zhí)行時,要手動kill進(jìn)程才會觸發(fā)debug,點IDE上的關(guān)閉按鈕不會觸發(fā)
在DubboShutdownHook.doDestroy打上斷點,debug發(fā)現(xiàn)只會執(zhí)行一次,這說明spring和dubbo的ShutdownHook只會注冊一次,這是怎么實現(xiàn)的呢?經(jīng)過很多次測試,發(fā)現(xiàn)了dubbo一個很牛逼的設(shè)計。
DubboShutdownHook中有register和unregister方法,分別是注冊和注銷ShutdownHook,在這兩個方法上都打上斷點,在程序啟動時發(fā)現(xiàn)這樣一個有趣的執(zhí)行順序:
總結(jié)一下是dubbo本身注冊了ShutdownHook,但如果用到了spring框架,spring框架在初始化時注銷了dubbo注冊的ShutdownHook,這樣就只保留了spring的ShutdownHook,真是秒啊!實現(xiàn)的代碼只有這短短幾行
public static void addApplicationContext(ApplicationContext context) { CONTEXTS.add(context); if (context instanceof ConfigurableApplicationContext) { ((ConfigurableApplicationContext) context).registerShutdownHook(); DubboShutdownHook.getDubboShutdownHook().unregister(); } BeanFactoryUtils.addApplicationListener(context, SHUTDOWN_HOOK_LISTENER); }
于是懷疑的ShutdownHook問題被證明沒有任何問題了。
從注銷堆棧繼續(xù)排查
能穩(wěn)定復(fù)現(xiàn)的問題一定很好排查,借助IDE的debug來看兩次注銷的調(diào)用堆棧,在注冊中心擴展的unregister方法處加斷點,可以看到如下兩次來源不同的堆棧信息
代碼中體現(xiàn)是
也就是說一次ShutdownHook執(zhí)行,觸發(fā)了兩次注銷。
接下來就比較好排查了,一步一步debug,這里解釋下
AbstractRegistryFactory.destroyAll()是銷毀所有注冊中心,銷毀時會調(diào)研注冊中心的注銷接口
destroyProtocols是銷毀所有的protocol,注冊中心的protocol在銷毀時拿到registry,然后調(diào)用了registry的注銷接口
那么dubbo 2.7.7是如何避免這個問題的呢?
在dubbo 2.7.7的代碼中,注冊中心的protocol在銷毀時獲取注冊中心稍微增加了點代碼
原來在注冊中心被銷毀后,destroyed變量被置為true,從而在registry protocol再次獲取注冊中心時,已經(jīng)拿不到了原先的注冊中心了,拿到的是一個空的注冊中心,調(diào)用注銷,自然沒有什么效果。
追溯了下github,這次PR是
https://github.com/apache/dubbo/pull/5450
這個修復(fù)在2.7.5就已經(jīng)修復(fù)了
dubbo重復(fù)注銷問題存在于2.7.0 ~ 2.7.4版本,2.7.5修復(fù),zk注冊中心不會報錯,可能無法感知,但它確實存在,也會拖慢應(yīng)用的關(guān)閉速度
通過追查發(fā)現(xiàn),其實該問題可以在注冊中心的擴展中解決,讓registry的destroy只能被調(diào)用一次
遇到無論多小的問題,有空都去鉆研下,你會收貨一些新知識,比如這次dubbo中ShutdownHook如此巧妙的設(shè)計
以上就是如何排查Dubbo接口重復(fù)注銷問題,小編相信有部分知識點可能是我們?nèi)粘9ぷ鲿姷交蛴玫降?。希望你能通過這篇文章學(xué)到更多知識。更多詳情敬請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。