阿里面试题:使用dubbo过程中遇到过哪些坑?
點擊上方“朱小廝的博客”,選擇“設為星標”
后臺回復”加群“加入公眾號專屬技術群
Dubbo[? |?d?b??| ,發音為`打波`] 穩如狗,哪有坑?
如果你用過Dubbo,但是沒碰到過什么坑,那只能說明你還沒有深交Dubbo,看看筆者那些年使用Dubbo踩過的坑!
父子類有相同屬性時值丟失
假設Provider提供的服務中某個服務的參數是WordDTO,并且WordDTO繼承自BaseDTO,兩個類的定義如下:
@Data public?class?BaseDTO?implements?Serializable?{private?Long?id; }@Data public?class?WordDTO?extends?BaseDTO?{private?Long?id;private?String?uuid;private?Long?timestamp;private?String?word; }問題描述:在Consumer側給WordDTO賦的值,其id屬性的值無法在Provider側獲取到。假設Consumer傳的值是:{"id":68,"timestamp":1570928394380,"uuid":"f774f99f-987c-4506-8ab8-366cd619bb15","word":"hello world"},在Provider拿到的卻是:{"timestamp":1570928394380,"uuid":"f774f99f-987c-4506-8ab8-366cd619bb15","word":"hello world"}。
原因分析:dubbo默認采用的是hessian序列化&反序列化方式,JavaDeserializer在獲取fileds時,采用了Map去重。但是在讀取值時,根據serializer的順序,對于同名字段,子類的該字段值會被賦值兩次,總是被父類的值覆蓋,導致子類的字段值丟失。
解決方案:
更改序列化方式(不建議);
刪掉子類中與父類同名屬性(建議);
自定義異常被包裝成RuntimeException
首先需要說明的是,出現這個問題有一定的條件。如果Provider中的api和自定義Exception定義都是在一個api.jar中,那么是不會有任何問題的。但是如果自定義Exception是在一個單獨的比如common.jar包中就會出現這個問題(此時api和model在另一個api.jar中)。
下面是一段調用一個會拋出自定義異常的服務的代碼:
try?{String?hello?=?demoService.saySomething(wordDTO);System.out.println(hello); }catch?(WrongArgumentException?e){System.err.println("wrong?argument?1:?"?+?e.getMessage()); }catch?(RuntimeException?e){System.err.println("wrong?argument?2:?"?+?e.getMessage()); }但是,調用的日志卻是如下所示,通過日志我們可以發現,在Consumer中并沒有捕獲到自定義的WrongArgumentException異常,只能捕獲到RuntimeException中的異常,且這個異常信息是封裝自定義的WrongArgumentException異常:
wrong?argument?2:?com.afei.dev.maven.exception.WrongArgumentException:?word不允許為空 com.afei.dev.maven.exception.WrongArgumentException:?word不允許為空at?com.afei.test.dubbo.provider.facade.impl.DemoServiceImpl.saySomething(DemoServiceImpl.java:11)at?com.alibaba.dubbo.common.bytecode.Wrapper1.invokeMethod(Wrapper1.java)這是什么原因呢?這是因為dubbo Provider的ExceptionFilter.java對異常統一封裝導致的,其封裝的核心源碼我就不貼出來了,你如果有興趣可以自己下載查看。我這里只貼出它的處理邏輯,當碰到如下這些情況時,dubbo會直接拋出異常:
如果是checked異常(不是RuntimeException但是是Exception.java類型的異常),直接拋出;
在方法簽名上有聲明(例如String saySomething()throws MyException ),直接拋出;
異常類和接口類在同一jar包里,直接拋出;
是JDK自帶的異常(全類名以java或者javax開頭,例如java.lang.IllegalStateException),直接拋出;
是Dubbo本身的異常RpcException,直接拋出;
否則,Dubbo通過如下代碼將異常包裝成RuntimeException拋給客戶端:
return?new?RpcResult(new?RuntimeException(StringUtils.toString(exception)));通過上面對ExceptionFilter的源碼分析可知,如果要讓Provider拋出自定義異常,有如下幾個解決辦法:
將自定義異常和接口類放到一個包中即可(推薦);
方法簽名上申明自定義異常;
那么Dubbo為什么這樣設計?我相信沒有誰比Dubbo的作者梁飛更有發言權了!這里就引用Dubbo作者梁飛在Github上的原話(原話出處:https://github.com/apache/dubbo/issues/111):
這個是為了防止服務提供方拋出了消費方沒有的異常,比如數據庫異常類,導致消費方反序列化失敗,使異常信息更奇怪,建議在業務接口上RuntimeException也聲明在throws中。
IP暴露問題
在某些復雜環境下,例如Docker、雙網卡、虛擬機等環境下,Dubbo默認綁定的IP可能并不是我們期望的正確IP,Dubbo綁定IP默認行為如下(核心源碼在NetUtils.java中):
通過InetAddress.getLocalHost()獲取本機地址,如果本機地址有效則返回(有效地址需要滿足這幾點:1. 不能為空,2. 不是loopback地址(類似127.x.x.x),3. 不能是0.0.0.0,也不能是127.0.0.1);
如果本機地址無效,那么再遍歷網卡地址,然后通過isValidAddress校驗ip是否正常并返回第一個有效的IP地址。這樣的話,Dubbo就不能保證返回的是內網IP還是外網IP。
事實上,復雜環境下這個IP綁定問題不太好自動化解決,不過我們可以利用dubbo的擴展能力解決這些問題。
Docker環境
如果你的dubbo部署在Docker上,那么需要注意了。我們需要解決Dubbo幾個特定參數來解決這個問題:
DUBBO_IP_TO_REGISTRY --- Registering to the IP address of the registration center
DUBBO_PORT_TO_REGISTRY --- Registering to the port of the registration center
DUBBO_IP_TO_BIND --- Listening IP addresses
DUBBO_PORT_TO_BIND --- Listening ports
假設主機IP地址為30.5.97.6,docker啟動dubbo服務參考命令,啟動后,這個Provider服務注冊的地址就是30.5.97.6:20881,我們可以通過命令(telnet 30.5.97.6 20881,invoke org.apache.dubbo.test.docker.DemoService.hello("world"))檢查并調用Provider提供的服務:
docker?run?-e?DUBBO_IP_TO_REGISTRY=30.5.97.6?-e?DUBBO_PORT_TO_REGISTRY=20881?-p?30.5.97.6:20881:20880?--link?zkserver:zkserver?-it?--rm?dubbo-docker-sample參考地址:https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-docker。
暴露外網IP
如果你服務的調用方和消費方不在同一個內網中,那么就會希望Dubbo服務通過外網IP暴露。不過不好意思,dubbo默認的服務暴露行為搞不定,因為dubbo默認暴露的是內網IP地址。
這個時候,我們就需要借助兩個參數:dubbo.protocol.host和dubbo.protocol.port,通過這兩個參數顯示申明我們暴露服務的IP和Port,這兩個參數即可以通過配置文件方式指定,也可以通過JVM參數方式指定,具體怎么使用,UP TO YOU!!!
雙網卡問題
當服務器上有多個網卡時,Dubbo服務提供者啟動時,會將錯誤的IP注冊到注冊中心,從而導致消費端連接不上。這種情況的筆者提供兩種解決辦法:
配置dubbo.protocol.host=192.168.0.1
配置/etc/hosts,例如afeiserver01 = 192.168.0.1,其中afeiserver01是機器名;
Data length too large
這個異常的詳細堆棧信息如下所示:
org.apache.dubbo.remoting.transport.ExceedPayloadLimitException: Data?length?too?large:?10356612,?max?payload:?8388608, channel:?NettyChannel?[channel=[id:?0xd36132c0,?L:/192.168.1.6:55078?-?R:/192.168.1.6:20880]]日志中提到max payload為8388608,等價于8 * 1024 * 1024,即8k。所以這個問題的原因非常清晰了,就是請求或者響應的報文體長度超過了8k。
這個問題比較簡單,筆者在這里提供兩個解決方案:
修改payload的值,將其調大,例如16777216,即16k,不推薦;
減少請求/響應報文長度。例如Provider提供的服務,最大批量限制為1000,比如最多只能批量查詢1000個用戶ID的用戶信息,推薦;
說明:
dubbo在小報文的場景下表現最佳,所以,除非確實無法饒過。否則強烈不建議調大payload的值;
線程耗盡
dubbo服務Provider側如果線程耗盡,會跑出類似如下的異常信息:
19-10-17?00:00:00.033?[New?I/O?server?worker?#1-6]?WARN??com.alibaba.dubbo.common.threadpool.support.AbortPolicyWithReport?-??[DUBBO]?Thread?pool?is?EXHAUSTED!?Thread?Name:?DubboServerHandler-10.0.0.77:20703,?Pool?Size:?200?(active:?200,?core:?200,?max:?200,?largest:?200),?Task:?5897697?(completed:?5897197),?Executor?status:(isShutdown:false,?isTerminated:false,?isTerminating:false),?in?dubbo://10.0.0.77:20703!,?dubbo?version:?2.5.3,?current?host:?127.0.0.1對dubbo有基本了解的都知道,Provider默認是fixed線程池,且線程數為200。那么什么時候會出現這種異常呢:
Provider側接口處理太慢。如果是這種原因的話,我們可以通過jstack命令,在線程棧中看到超過200個狀態為RUNNING、且命名為"DubboServerHandler-"的線程,通過線程棧我們大概知道是哪部分代碼引起的,然后優化問題代碼,提升處理能力從而解決這個問題;
Provider處理能力確實不夠。這個原因是指,Consumer可能會達到10000TPS,但是Provider單機處理能力可能只有1000TPS,如果沒有10臺以上的Provider服務實例,那么就確實需要擴容了。
Provier由于某些原因阻塞。這個原因一般是Provider側依賴的某些服務或者中間件出問題導致的;
根據下面這段日志可知,Dubbo線程都阻塞在發送ActiveMQ消息的地方,我們可以通過異步發送MQ消息,或者檢查是不是ActiveMQ服務吞吐量不行并優化它的吞吐量等手段來解決:
"DubboServerHandler-127.0.0.1:20880-thread-128"?daemon?prio=10?tid=0x00007fd574193811?nid=0x16cf1?waiting?for?monitor?entry?[0x00007fd691887000..0x00007fd691888810]java.lang.Thread.State:?BLOCKED?(on?object?monitor)at?org.apache.activemq.transport.MutexTransport.oneway(MutexTransport.java:40)-?waiting?to?lock?<0x00007fd6c9fa4ba8>?(a?java.lang.Object)at?org.apache.activemq.transport.ResponseCorrelator.oneway(ResponseCorrelator.java:60)at?org.apache.activemq.ActiveMQConnection.doAsyncSendPacket(ActiveMQConnection.java:1265)at?org.apache.activemq.ActiveMQConnection.asyncSendPacket(ActiveMQConnection.java:1259)服務調用失敗
異常堆棧信息如下所示:
Forbid?consumer?0?access?service?com.afei.dubbo.demo.api.QueryService?from?registry?127.0.0.1:2181?use?dubbo?version?2.5.3, Please?check?registry?access?list?(whitelist/blacklist)或者異常堆棧信息如下所示:
org.apache.dubbo.rpc.RpcException: Failed?to?invoke?the?method?saySomething?in?the?service?com.afei.test.dubbo.provider.facade.DemoService. No?provider?available?for?the?service?com.afei.test.dubbo.provider.facade.DemoService:2.0.0 from?registry?224.5.6.7:1234我相信,每一個使用過dubbo服務的同學,肯定會碰到上面這兩個ERROR日志。這兩個問題一般有如下幾種原因:
Provider服務全部下線,即沒有一個存活的Provider服務進程。
存在一個或多個Provider服務,但是version或者group不匹配。例如Consumer側申明version=1.0.0,而Provider側申明version=2.0.0,或者group不匹配,都會出現這個ERROR。
暴露的IP有問題。例如暴露的是內網IP,但是調用卻是通過外網IP;
dubbo spring schema
在dubbo進入apache之前,dubbo的spring schema申明如下:
http://code.alibabatech.com/schema/dubbo當dubbo進入apache后,dubbo的spring schema能兼容兩種方式:
http://dubbo.apache.org/schema/dubbo http://code.alibabatech.com/schema/dubbo這是由dubbo.jar中META-INF/spring.schemas文件決定的:
http\://dubbo.apache.org/schema/dubbo/dubbo.xsd=META-INF/dubbo.xsd http\://code.alibabatech.com/schema/dubbo/dubbo.xsd=META-INF/compat/dubbo.xsddubbo新版本是即能兼容http://dubbo.apache.org/schema/dubbo,也能兼容http://code.alibabatech.com/schema/dubbo。但是dubbo老版本只能兼容http://code.alibabatech.com/schema/dubbo。如果老版本也配置http://dubbo.apache.org/schema/dubbo,就會拋出如下日常:
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration?problem:?Unable?to?locate?Spring?NamespaceHandler?for?XML?schema?namespace?[http://dubbo.apache.org/schema/dubbo]想知道更多?掃描下面的二維碼關注我
當當1024圖書優惠活動,疊加科技類圖書每滿100-50(活動時間10月22~24)。使用我的優惠嗎:2CGJZK,還可以享受:實付200-30(全場自營圖書可用,優惠券到期時間11-01),實際為花170買400元圖書。推薦書籍除了我的《深入理解Kafka》和《RabbitMQ實戰指南》之外,還推薦《數據密集型應用系統設計》、《SRE: Google運維解密》、《高性能MySQL》、《實現領域驅動設計》等。
朕已閱?
總結
以上是生活随笔為你收集整理的阿里面试题:使用dubbo过程中遇到过哪些坑?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 比Redis快5倍的中间件,为啥这么快?
- 下一篇: 干货 | 阿里巴巴HBase高可用8年抗