c++ 外部组件发生异常_谁再悄咪咪的吃掉异常,我上去就是一 JIO
又到周末了,周更選手申請(qǐng)出站~
這圖太魔性了啊
這次分享一下上個(gè)月碰到的離奇的問(wèn)題。一個(gè)簡(jiǎn)單的問(wèn)題,硬是因?yàn)楫惓1磺倪溥涑缘?#xff0c;過(guò)關(guān)難度直線提升,導(dǎo)致小黑哥排查一個(gè)晚上。
這個(gè)美好的晚上,本想著開兩把 LOL 無(wú)限火力,在召喚師峽谷來(lái)個(gè)五殺的~
哎,就這樣沒(méi)了啊!我知道,你們一定能理解這種五殺被搶的感覺(jué)~
下次,真的,誰(shuí)再讓我看到悄咪咪的吃掉異常,我真的要上去一 Jio 了!
好了,本文可不是水文,看完本篇文章,你可以學(xué)到以下知識(shí)點(diǎn):
- Arthas 排查技巧
- 啥是 NoClassDefFoundError
- Dubbo 異常內(nèi)部處理方式
好了,同學(xué)們,打開小本子,準(zhǔn)備記好知識(shí)點(diǎn)~
“
先贊后看,養(yǎng)成習(xí)慣~微信搜索「程序通事」,關(guān)注就完事了!
起因
我們有個(gè)業(yè)務(wù)系統(tǒng),應(yīng)用之間調(diào)用鏈如下所示:
A 應(yīng)用是業(yè)務(wù)發(fā)生起始應(yīng)用,在這個(gè)應(yīng)用中將會(huì)根據(jù)一定規(guī)則選擇最后的通訊渠道 C,然后將這個(gè)渠道標(biāo)識(shí)傳遞給 B 應(yīng)用。
B 應(yīng)用的功能類似網(wǎng)關(guān),這個(gè)應(yīng)用將會(huì)根據(jù) A 應(yīng)用傳遞過(guò)來(lái)的渠道標(biāo)識(shí),將會(huì)請(qǐng)求路由下發(fā)到具體的 C 應(yīng)用,起到服務(wù)路由的功能。
C 應(yīng)用是與外部應(yīng)用交互的應(yīng)用,我們將其稱為渠道通訊機(jī)。
假設(shè)一次業(yè)務(wù)中,A 應(yīng)用根據(jù)規(guī)則選擇 C2 的渠道標(biāo)識(shí),然后傳遞給 B 應(yīng)用。B 應(yīng)用根據(jù)這個(gè)標(biāo)識(shí)選擇使用 C2 進(jìn)行通訊,最后 C2 調(diào)用外部應(yīng)用完成一次業(yè)務(wù)調(diào)用。
上述所有應(yīng)用都基于 Dubbo 進(jìn)行遠(yuǎn)程通訊,B 應(yīng)用實(shí)現(xiàn)原理在小黑哥之前文章「支付路由系統(tǒng)演進(jìn)史」中有寫過(guò),感興趣的同學(xué)可以查看一下。
介紹完業(yè)務(wù)的基本情況,現(xiàn)在我們來(lái)看下到底發(fā)生了啥事。
一次業(yè)務(wù)需求中,需要改動(dòng) C2 應(yīng)用,這次改動(dòng)功能點(diǎn)真的很小,很快就完成了。小黑哥想著閑著也是閑著,于是就把之前 C2 應(yīng)用中打印的日志中一些沒(méi)有脫敏的信息,進(jìn)行脫敏處理。
由于之前日志框架脫敏處理存在一些問(wèn)題,于是就將日志框架從 Log4j 升級(jí)為 LogBack。升級(jí)之后,為了防止不同日志框架中之間的產(chǎn)生沖突,于是使用 IDEA Maven Helper 插件,統(tǒng)一將應(yīng)用中所有的 Log4j 相關(guān)依賴都給排除了。
改動(dòng)完成之后,將 C2 應(yīng)用發(fā)布到測(cè)試環(huán)境,再次從 A 應(yīng)用發(fā)起測(cè)試, B 應(yīng)用返回異常提示未找到 C2 應(yīng)用。
B 應(yīng)用業(yè)務(wù)代碼類似如下:
public?Response?pay(Request?req)?{????????try?{????????if?(!isSupport(req.getChnlCode()))?{????????????return?new?Response("ERROR",?"未找到相關(guān)渠道應(yīng)用");????????}????????return?doPay(req);????}?catch?(Exception?e)?{????????return?new?Response("ERROR",?"未找到相關(guān)渠道應(yīng)用");????}}正常情況下,若是配置存在問(wèn)題,B 應(yīng)用將會(huì)返回未找到具體渠道,請(qǐng)求也會(huì)在 B 應(yīng)用結(jié)束,不會(huì)調(diào)用到 C2 應(yīng)用(也沒(méi)辦法調(diào)用)。
然而此次配置什么都沒(méi)問(wèn)題, 而且最詭異的是 C2 應(yīng)用居然收到了請(qǐng)求,并且成功處理了業(yè)務(wù)請(qǐng)求。
排查問(wèn)題
由于 B 應(yīng)用異常處理時(shí),將異常吃掉了,我們沒(méi)辦法得知這個(gè)過(guò)程到底發(fā)生了啥事,所以第一要緊的事獲取異常信息。
最簡(jiǎn)單的辦法就是,將 B 應(yīng)用改造一下,加入打印異常日志。不過(guò)當(dāng)時(shí)比較懶,不想改造應(yīng)用,就想獲取異常信息,于是想到使用 **Arthas**[1]。
Arthas 排錯(cuò)技巧
Arthas 是Alibaba開源的Java診斷工具,這里就不再詳細(xì)介紹這個(gè)工具,主要講下這次排錯(cuò)用到的命令-**watch**[2]。
watch 命令可以方便觀察指定方的調(diào)用情況,可以具體觀察方法的返回值、拋出異常、入?yún)?#xff0c;另外還可以通過(guò) OGNL表達(dá)式查看對(duì)應(yīng)的變量。
這里我們主要為了查看方法拋出的異常信息,執(zhí)行命令如下:
watch?com.dubbo.example.DemoService?doPay?-e?-x?2?'{params,throwExp}'“
上述命令將會(huì)在方法異常之后觀察方法的入?yún)⒁约爱惓P畔ⅰ?/p>
注意,我們需要查看 doPay 方法,而不是 pay 方法。這是因?yàn)?pay方法中我們將異常捕獲,不太可能會(huì)拋出異常哦~
異常信息如下所示:
真正引起此次錯(cuò)誤的異常信息為:
java.lang.NoClassDefFoundError:?Could?not?initialize?class?xx.xxx.xx.GELogger由于此次 B 應(yīng)用不存在改動(dòng),所以推測(cè)這個(gè)異常實(shí)際發(fā)生在 C2 應(yīng)用,于是在 C2 應(yīng)用處再次使用 Arthas watch 命令,同樣觀察到相同的錯(cuò)誤信息。
NoClassDefFoundError
NoClassDefFound,從名字上我們可以推測(cè)是因?yàn)轭惒淮嬖?#xff0c;從而引發(fā)的這個(gè)錯(cuò)誤。按照這個(gè)思路,我們首先可以簡(jiǎn)單查看一下 B 應(yīng)用中是否存在 GELogger 相關(guān)類。
查看 B 應(yīng)用相關(guān)依賴包,從中發(fā)現(xiàn)了這個(gè)類文件,這說(shuō)明這個(gè)類確實(shí)存在。
在 IDEA 反編譯查看 GELogger類相關(guān)源碼,從中發(fā)現(xiàn)了問(wèn)題。
private?static?Logger?logger;static?{????System.out.println("static?init");????logger?=?Logger.getLogger(NoClassDefFoundErrorTestService.class);????System.out.println("Logger?init?success");}GELogger存在一個(gè)靜態(tài)代碼塊,用于初始化一個(gè) org.apache.log4j.Logger日志類。
然后在上面改動(dòng)中,全部的 Log4j依賴都被排除了,所以這里運(yùn)行時(shí)應(yīng)該會(huì)拋出另外一個(gè)找到 org.apache.log4j.Logger 錯(cuò)誤。
執(zhí)行以下代碼,模擬拋錯(cuò)過(guò)程。
System.out.println("模擬第一次?Error");try?{????NoClassDefFoundErrorTestService?noClassDefFoundErrorTestService=new?NoClassDefFoundErrorTestService();}?catch?(Throwable?e)?{????e.printStackTrace();}System.out.println("模擬第二次?Error");try?{????NoClassDefFoundErrorTestService?noClassDefFoundErrorTestService=new?NoClassDefFoundErrorTestService();}?catch?(Throwable?e)?{????e.printStackTrace();}異常信息如下所示:
異常信息
第一次創(chuàng)建 NoClassDefFoundErrorTestService實(shí)例時(shí),Java 虛擬機(jī)讀取加載時(shí),將會(huì)初始化靜態(tài)代碼塊時(shí)。由于 org.apache.log4j.Logger類不存在,靜態(tài)代碼塊執(zhí)行異常,從而導(dǎo)致類加載失敗。
第二次再創(chuàng)建 NoClassDefFoundErrorTestService 實(shí)例時(shí),Java 虛擬機(jī)不會(huì)再次讀取加載,所以直接返回了以下異常。
java.lang.NoClassDefFoundError: Could not initialize class com.dubbo.example.NoClassDefFoundErrorTestService找到問(wèn)題真正原因,解決辦法也很簡(jiǎn)單,直接排除 GELogger 所在依賴包。
Dubbo 內(nèi)部異常處理
雖然問(wèn)題到此解決了,但是這里還有一個(gè)疑問(wèn),為何 C2 應(yīng)用發(fā)生了異常,卻沒(méi)有相關(guān)錯(cuò)誤日志,并且 C2 業(yè)務(wù)邏輯也正常處理完成。
這就要說(shuō)到 Dubbo 內(nèi)部異常錯(cuò)誤處理方式,上面 GELogger 其實(shí)作用在一個(gè) Dubbo 自定義 Filter 中,用來(lái)記錄結(jié)果,模擬代碼如下:
@Activate(????????group?=?{"provider",?"consumer"})public?class?ErrorFilter?implements?Filter?{????@Override????public?Result?invoke(Invoker>?invoker,?Invocation?invocation)?throws?RpcException?{????????Result?result?=?invoker.invoke(invocation);????????NoClassDefFoundErrorTestService?noClassDefFoundErrorTestService=new?NoClassDefFoundErrorTestService();????????//?處理業(yè)務(wù)邏輯????????return?result;????}}這個(gè)自定義 Filter 中首先執(zhí)行 invoker 方法,這個(gè)方法將會(huì)調(diào)用真正的業(yè)務(wù)方法,這就是為什么 C2 應(yīng)用邏輯是正常處理完成。
業(yè)務(wù)方法處理完成之后,然后執(zhí)行后續(xù)邏輯。由于 NoClassDefFoundErrorTestService將會(huì)拋出 Error,最終這個(gè) Error,將會(huì)在 HeaderExchangeHandler#handleRequest 被捕獲,然后將會(huì)把相關(guān)異常信息返回給調(diào)用 Dubbo 消費(fèi)者。
Dubbo 2.7
而在 Dubbo 消費(fèi)者接受到服務(wù)提供者返回信息之后,將會(huì)在 DefaultFuture#doReceived轉(zhuǎn)化成 RemotingException。
dubbo consumer 2.7
而 RemotingException 最終將會(huì)在 FailoverClusterInvoker#doInvoke 轉(zhuǎn)換成 RpcException返回給業(yè)務(wù)代碼。
總結(jié)
好了,說(shuō)了這么多,總結(jié)一下本文知識(shí)點(diǎn)
參考資料
[1] Arthas: https://alibaba.github.io/arthas/index.html
[2] watch: https://alibaba.github.io/arthas/watch.html
[3] 當(dāng)Dubbo遇上Arthas:排查問(wèn)題的實(shí)踐: https://dubbo.apache.org/zh-cn/blog/dubbo-meet-arthas.html
[4] java.lang.NoClassDefFoundError 的解決方法一例: https://www.codelast.com/原創(chuàng)-java-lang-noclassdeffounderror-的解決方法一例/
[5] noclassdeffounderror-could-not-initialize-class-error: https://stackoverflow.com/questions/1401111/noclassdeffounderror-could-not-initialize-class-error
總結(jié)
以上是生活随笔為你收集整理的c++ 外部组件发生异常_谁再悄咪咪的吃掉异常,我上去就是一 JIO的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python在线编辑器最新_Editor
- 下一篇: net 模式中虚拟机连不上本机oracl