面試官問我,使用Dubbo有沒有遇到一些坑?我笑了。
前言
17年的時候,因為一時衝動沒把持住(當然最近也有粉絲叫我再衝動一把再更新一波),結合面試題寫了一個系列的Dubbo原始碼解析.目前公眾號大部分粉絲都是之前的粉絲,這裡不過多介紹.
根據我的面試經驗而言,能在簡歷上寫上原理、原始碼等關鍵詞的,是非常具備核心競爭力的.上週和一個公眾號粉絲交流面試情況如下
面試的時候,把原始碼一波分析,令面試官虎軀一震!在一陣前戲過後,以為接下來無非就是身體的一頓抽搐一切變得索然無味,不料面試官來了句令劇情發生了反轉
"你對Dubbo原始碼這麼熟悉,那請問你使用的時候,有沒有遇到什麼坑"
我擦,毫無準備的他,菊花頓時一緊!此時就面臨唬住了50K,唬不住就只能5K的局面,慌了!
論如何反殺
相信大家面試都遇到過類似問題,因為原始碼解析網上很多,很多人"考前突擊"一下,但是遇到喜歡問細節的面試官,終究難逃法眼,無處遁形.遇到這個問題,我們如何反殺一波?那麼我就從一次聊天記錄說起,畢竟只有關注肥朝公眾號,擁有真實場景的原始碼實戰(非常重要),遇到這類問題,才不至於出現猛虎落淚的情形
真實場景描述
那麼我們把業務相關去掉,抽取一個最簡模型.我們在公司,一般都會有自己的自定義異常,然後這個自定義異常一般放在common.jar給其他模組依賴,比如我這裡定義一個HelloException
1public class HelloException extends RuntimeException {23 public HelloException() {4 }56 public HelloException(String message) {7 super(message);8 }910}
然後我們寫一個最簡單的Dubbo的demo,如下
interface
1public interface DemoService {23 String sayHello(String name);45}
provider
1public class DemoServiceImpl implements DemoService {23 public String sayHello(String name) {4 throw new HelloException("公眾號:肥朝");5 }67}
consumer
1public class DemoAction {23 private DemoService demoService;45 public void setDemoService(DemoService demoService) {6 this.demoService = demoService;7 }89 public void start() throws Exception {10 try {11 String hello = demoService.sayHello("公眾號:肥朝");12 } catch (HelloException helloException) {13 System.out.println("這裡捕獲helloException異常");14 }15 }1617}
按照聊天記錄的描述,此時consumer呼叫provider,provider丟擲HelloException.但是consumer捕獲到的,卻不是HelloException.
那麼我們執行看看
果然如該同事所言.為什麼會這樣呢?之前沒看過肥朝Dubbo原始碼解析系列的同學這種時候往往採用最低效的解決辦法,把異常棧往微信群一丟,各種求助.但是往往毫無收穫,然後感嘆社會為何如此冷漠!
但是相信公眾號的老粉絲們早已掌握閱讀原始碼的技能,和肥朝一樣坐懷不亂,九淺一深直入原始碼.出現異常我們首先看一下異常棧
除非擼多了看不清(建議戒擼),否則這行異常和肥朝一樣,就像漆黑中的螢火蟲一樣,那麼鮮明,那麼出眾
1com.alibaba.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:108)
那麼我們一探究竟
1 public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {2 try {3 Result result = invoker.invoke(invocation);4 if (result.hasException() && GenericService.class != invoker.getInterface()) {5 try {6 Throwable exception = result.getException();78 // 如果是checked異常,直接丟擲9 if (! (exception instanceof RuntimeException) && (exception instanceof Exception)) {10 return result;11 }12 // 在方法簽名上有宣告,直接丟擲13 try {14 Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());15 Class<?>[] exceptionClassses = method.getExceptionTypes(); 16 for (Class<?> exceptionClass : exceptionClassses) {17 if (exception.getClass().equals(exceptionClass)) {18 return result;19 }20 }21 } catch (NoSuchMethodException e) {22 return result;23 }2425 // 未在方法簽名上定義的異常,在伺服器端列印ERROR日誌26 logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost()27 + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()28 + ", exception: " + exception.getClass().getName() + ": " + exception.getMessage(), exception);2930 // 異常類和介面類在同一jar包裡,直接丟擲31 String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());32 String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());33 if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)){34 return result;35 }36 // 是JDK自帶的異常,直接丟擲37 String className = exception.getClass().getName();38 if (className.startsWith("java.") || className.startsWith("javax.")) {39 return result;40 }41 // 是Dubbo本身的異常,直接丟擲42 if (exception instanceof RpcException) {43 return result;44 }4546 // 否則,包裝成RuntimeException拋給客戶端47 return new RpcResult(new RuntimeException(StringUtils.toString(exception)));48 } catch (Throwable e) {49 logger.warn("Fail to ExceptionFilter when called by " + RpcContext.getContext().getRemoteHost()50 + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()51 + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);52 return result;53 }54 }55 return result;56 } catch (RuntimeException e) {57 logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost()58 + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()59 + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);60 throw e;61 }62 }
1.如果是checked異常,直接丟擲.很明顯,我們的HelloException是RuntimeException,不符合2.在方法簽名上有宣告,直接丟擲.很明顯,我們介面並未宣告該異常,不符合3.異常類和介面類在同一jar包裡,直接丟擲.很明顯,我們的異常類是在common.jar的,介面是在api.jar的,不符合4.是JDK自帶的異常,直接丟擲.很明顯,這個HelloException是我們自定義的,不符合5.是Dubbo本身的異常(RpcException),直接丟擲.很明顯,這個HelloException是我們自定義的,和RpcException幾乎沒有半毛錢關係.6.否則,包裝成RuntimeException拋給客戶端.因為以上5點均不滿足,所以該異常會被包裝成RuntimeException異常丟擲(重要)
這也就是為什麼我們catchHelloException是catch不到的,因為他包裝成RuntimeException了
Dubbo為什麼這麼設計
也許你看到這裡會覺得這個判斷好坑.Dubbo為什麼要這麼設計?我們看原始碼,最重要的是知道作者為什麼這麼設計,只有知道為什麼這麼設計才是經過了深度的思考,否則看時高潮,看後就忘.講清楚為什麼這麼設計,也是大家關注肥朝公眾號的一個重要原因.
其實Dubbo的這個考慮,是基於序列化來考慮的.你想想,如果provider丟擲一個僅在provider自定義的一個異常,那麼該異常到達consumer,明顯是無法序列化的.所以你注意看Dubbo的判斷.我們來看下他的判斷
1.如果是checked異常,直接丟擲.很明顯,我們的HelloException是RuntimeException,不符合2.在方法簽名上有宣告,直接丟擲.很明顯,我們介面並未宣告該異常,不符合3.異常類和介面類在同一jar包裡,直接丟擲.很明顯,我們的異常類是在common.jar的,介面是在api.jar的,不符合4.是JDK自帶的異常,直接丟擲.很明顯,這個HelloException是我們自定義的,不符合5.是Dubbo本身的異常(RpcException),直接丟擲.很明顯,這個HelloException是我們自定義的,和RpcException幾乎沒有半毛錢關係.6.否則,包裝成RuntimeException拋給客戶端.因為以上5點均不滿足,所以該異常會被包裝成RuntimeException異常丟擲(重要)
如何解決
既然都知道了原理了,那麼很好解決,我隨便列舉一下,比如從規範上要求業務方介面宣告HelloException
寫在最後
當然肥朝面試的時候,也曾經被問過類似問題,你用XXX有沒有遇到過什麼坑.在一波操作猛如虎的分析下,面試官說
"你真帥".
肥朝會心一笑
結果他卻說
"你笑起來更帥"!
免費Java資料領取,涵蓋了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo/Kafka、Hadoop、Hbase、Flink等高併發分散式、大資料、機器學習等技術。 傳送門: https://jq.qq.com/?_wv=1027&k=5F79bMq
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69912582/viewspace-2638416/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 面試官問我:建立執行緒有幾種方式?我笑了面試執行緒
- 面試官問,你使用過命令模式嗎?我笑了!面試模式
- 當面試官問我JDK Semaphore的原理時,我笑了面試JDK
- 我說我精通字串,面試官竟然問我Java中的String有沒有長度限制!?字串面試Java
- 當面試官問我Mybatis初始化原理時,我笑了面試MyBatis
- 面試官問我:spring、springboot、springcloud的區別,我笑了面試Spring BootGCCloud
- 面試的反殺-你有沒有想要問我的面試
- 我說我精通字串,面試官竟然問我Java中的String有沒有長度限制!?|附視訊講解字串面試Java
- 面試官:你還有什麼想問我的?面試
- 我是設計師面試官,你有什麼想問我的?面試
- 面試官問我MySQL索引,我面試MySql索引
- 笑了,面試官問我知不知道非同步程式設計的Future。面試非同步程式設計
- 面試官問我HTTP,我真的是面試HTTP
- 面試官問我Redis叢集,我真的是面試Redis
- 面試官問我MySQL調優,我真的是面試MySql
- 面試官問我JVM調優,我忍不住了!面試JVM
- 面試官,我會寫二分查詢法了!對,沒有 bug 的那種!面試
- 面試官:Dubbo是什麼,他有什麼特性?面試
- 面試官問我會不會Elasticsearch,我語塞了...面試Elasticsearch
- 【MySQL】面試官問我:MySQL如何實現無資料插入,有資料更新?我是這樣回答的!MySql面試
- 面試官問我JVM記憶體結構,我真的是面試JVM記憶體
- 想換坑了有沒有坑位
- 我以為我對索引非常瞭解,直到我遇到了阿里面試官...索引阿里面試
- 面試官:select、poll、epoll有何區別?我:阿巴阿巴...面試
- 我做技術面試官的一些體會面試
- 如果我是推薦演算法面試官,我會問哪些問題?演算法面試
- 面試官問我:什麼是JavaScript閉包,我該如何回答面試JavaScript
- 面試官問我redis資料型別,我回答了8種面試Redis資料型別
- 我以為我對Mysql索引很瞭解,直到我遇到了阿里的面試官MySql索引阿里面試
- Oracle有沒有MySQL的分割槽DDL遇到的問題OracleMySql
- 面試官: 我必問的容器知識點!面試
- 面試官,你再問我 Bit Operation 試試?面試
- 拜託,面試官別問我「布隆」了面試
- 阿里面試官用HashMap把我問倒了阿里面試HashMap
- 壞了!面試官問我垃圾回收機制面試
- 大家快看哪,JClass的一段原始碼,不是程式碼有問題就是我有問題,不過我想我沒問題。原始碼
- 有人問我去阿里有沒有捷徑,我只能告訴你有,它只是一個網約車專案,反正我靠他定級了P7阿里
- 面試官問:Java 執行緒沒有 Running 狀態,為什麼?面試Java執行緒