国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

如何抓到Dubbo異步調用的小BUG

開發 前端
排查過程中還搜索了github,但沒有什么發現,說明這個BUG遇到的人很少,可能是大家用異步調用本來就很少,再加上返回基本類型就更少,所以也不奇怪。

hello,大家好呀,我是小樓。

最近一個技術群有同學at我,問我是否熟悉Dubbo,這我熟啊~

圖片

他說遇到了一個Dubbo異步調用的問題,懷疑是個BUG,提到BUG我可就不困了,說不定可以水,哦不...寫一篇文章。

圖片

問題復現

遇到問題,尤其不是自己遇到的,必須要復現出來才好排查,截一個當時的聊天記錄:

圖片

他的問題原話是:

今天發現一個問題 有一個dubbo接口返回類型是boolean, 把接口從同步改成異步 server 端返回true 消費端卻返回false,把boolean改成Boolean就能正常返回結果 有碰到過這個問題嗎

注意幾個重點:

  • 接口返回類型是boolean
  • 同步改為異步調用返回的boolean和預期不符合
  • boolean基本類型改成包裝類型Boolean就能正常返回

聽到這個描述,我的第一反應是這個返回結果定義為boolean肯定有問題!

《Java開發手冊》中就強調了RPC接口返回最好不要使用基本類型,而要使用包裝類型:

圖片

但這個是業務編碼規范,如果RPC框架不能使用boolean作為返回值,豈不是個BUG?而且他強調了是同步改為異步調用才出現這種情況,說明同步沒問題,有可能是異步調用的鍋。

于是我順口問了Dubbo的版本,說不定是某個版本的BUG。得到回復,是2.7.4版本的Dubbo。

于是我拉了個工程準備復現這個問題。

哎,等等~

Dubbo異步調用的寫法可多了,于是我又問了下他是怎么寫的。

圖片

知道怎么寫的就好辦了,寫個Demo先:

定義Dubbo接口,一個返回boolean,一個返回Boolean

public interface DemoService {
boolean isUser();
Boolean isFood();
}

實現Provider,為了簡單,都返回true,并且打了日志

@Service
public class DemoServiceImpl implements DemoService {

@Override
public boolean isUser() {
System.out.println("server is user : true");
return true;
}

@Override
public Boolean isFood() {
System.out.println("server is food : true");
return true;
}
}

實現Consumer,為了方便調用,實現了一個Controller,為了防止本機調用,injvm設置為false,這里是經驗,injvm調用邏輯和遠程調用區別挺大,為了防止干擾,統一遠程調用。

@RestController
public class DemoCallerService {

@Reference(injvm = false, check = false)
private DemoService demoService;

@GetMapping(path = "/isUser")
public String isUser() throws Exception {
BlockingQueue<Boolean> q = new ArrayBlockingQueue<>(1);
RpcContext.getContext().asyncCall(
() -> demoService.isUser()
).handle(
(isUser, throwable) -> {
System.out.println("client is user = " + isUser);
q.add(isUser);
return isUser;
});
q.take();
return "ok";
}

@GetMapping(path = "/isFood")
public String isFood() throws Exception {
BlockingQueue<Boolean> q = new ArrayBlockingQueue<>(1);
RpcContext.getContext().asyncCall(
() -> demoService.isFood()
).handle(
(isFood, throwable) -> {
System.out.println("client is food = " + isFood);
q.add(isFood);
return isFood;
});
q.take();
return "ok";
}
}

啟動一個Provider,再啟動一個Consumer進行測試,果然和提問的同學表現一致:

  • 先調用isUser(返回boolean),控制臺打印:
// client ...
client is user = false
// server ...
server is user : true
  • 再調用isFood(返回Boolean),控制臺打印:
// client ...
client is food = true
// server ...
server is food : true

問題排查

  • Debug

先猜測一下是哪里的問題,server端返回true,應該問題不大,可能是client端哪里轉換出錯了。但這都是猜想,我們直接從client端接收到的數據開始,如果接收的數據沒問題,肯定就是后續處理出了點小差錯。

如果你非常熟悉Dubbo的調用過程,直接知道大概在這里

com.alibaba.dubbo.remoting.exchange.support.DefaultFuture#doReceived

我們打3個斷點:

圖片

圖片

斷點①為了證明我們的請求進來了

斷點②為了證明進了回調

斷點③為了能從接收到數據包的初始位置開始排查

按照我們的想法,執行順序應該是①、③、②,但是這里很奇怪,并沒有按照我們的預期執行,而是先執行①,再執行②,最后執行③!

這是為什么?對于排查問題中的這些沒有符合預期的蛛絲馬跡,要特別留心,很可能就是一個突破點。

于是我們對asyncCall這個方法進行跟蹤:

圖片

發現這里callable調用call返回了false,然后false不為null且不是CompletableFuture的實例,于是直接調用了CompletableFuture.completedFuture(o)。

看到這里估計有部分小伙伴發現了問題,正常情況下,Dubbo的異步調用,執行調用后,不會立馬得到結果,只會拿到一個null或者一個CompletableFuture,然后在回調方法中等待server端的返回。

這里的邏輯是如果返回的結果不為null且不為CompletableFuture的實例就直接將CompletableFuture設置為完成,立馬執行回調。

暫且不管這個邏輯。

我們先看為什么會返回false。這里的callable是Dubbo生成的一個代理類,其實就是封裝了調用Provider的邏輯,有沒有辦法看看他封裝的邏輯呢?有!用arthas。

  • arthas

我們下載安裝一個arthas,可以參考如下文檔:https://arthas.aliyun.com/doc/quick-start.html

attach到我們的Consumer進程上,執行sc命令(查看已加載的類)查看所有生成的代理類,由于我們的Demo就生成了一個,所以看起來很清晰

sc *.proxy0

圖片

再使用jad命令反編譯已加載的類:

jad org.apache.dubbo.common.bytecode.proxy0

圖片

看到這里估計小伙伴們又揭開了一層疑惑,this.handler.invoke就是去調用Provider,由于這里是異步調用,必然返回的是null,所以返回值定義為boolean的方法返回了false。

看到這里,估計小伙伴們對《Java開發手冊》里的規范有了更深的理解,這里的處理成false也是無奈之舉,不然難道返回true?屬于信息丟失了,無法區分是調用的返回還是其他異常情況。

我們再回頭看asyncCall:

圖片

圈出來的這段代碼令人深思,尤其是最后一行,為啥直接將CompletableFuture設置為完成?

從這個方法的名字能看出它是執行異步調用,但這里有行注釋:

//local invoke will return directly

首先這個注釋的格式上下不一,//之后講道理是需要一個空格的,我覺得這里提個PR改下代碼格式肯定能被接受~

其次local invoke,我理解應該是injvm這種調用,為啥要特殊處理?這個處理直接就導致了返回基本類型的接口在異步調用時必然會返回false的BUG。

我們測試一下injvm的調用,將demo中injvm參數改為true,Consumer和Provider都在一個進程中,果然和注釋說的一樣:

server is user : true
client is user = true

如何修復

我覺得這應該算是Dubbo的一個BUG,雖然這種寫法不提倡,但作為一款RPC框架,這個錯誤還是不應該。

修復的辦法就是在injvm分支這里加上判斷,如果是injvm調用還是保持現狀,如果不是injvm調用,直接忽略,走最后的return邏輯:

public <T> CompletableFuture<T> asyncCall(Callable<T> callable) {
try {
try {
setAttachment(ASYNC_KEY, Boolean.TRUE.toString());
final T o = callable.call();
//local invoke will return directly
if (o != null) {
if (o instanceof CompletableFuture) {
return (CompletableFuture<T>) o;
}
if (injvm()) { // 偽代碼
return CompletableFuture.completedFuture(o);
}
} else {
// The service has a normal sync method signature, should get future from RpcContext.
}
} catch (Exception e) {
throw new RpcException(e);
} finally {
removeAttachment(ASYNC_KEY);
}
} catch (final RpcException e) {
// ....
}
return ((CompletableFuture<T>) getContext().getFuture());
}

最后

排查過程中還搜索了github,但沒有什么發現,說明這個BUG遇到的人很少,可能是大家用異步調用本來就很少,再加上返回基本類型就更少,所以也不奇怪。

而且最新的代碼這個BUG也還存在,所以你懂我意思吧?這也是個提交PR的好機會~

不過話說回來,我們寫代碼最好還是要遵循規范,這些都是前人為我們總結的最佳實踐,如果不按規范來,可能就會有意想不到的問題。

當然遇到問題也不要慌,代碼就在那躺著,工具也多,還怕搞不定嗎?

責任編輯:武曉燕 來源: 捉蟲大師
相關推薦

2009-11-09 10:50:30

WCF異步調用

2009-10-20 16:48:30

C#委托

2021-01-28 11:40:34

Dubbo異步配置

2009-12-21 14:10:26

WCF異步調用

2009-07-01 14:23:46

JavaScript異

2009-07-01 14:37:14

JavaScript異

2009-07-01 14:31:01

JavaScript異

2021-03-29 09:26:44

SpringBoot異步調用@Async

2009-07-01 13:58:00

JavaScript異

2024-07-31 15:57:41

2024-10-15 10:28:43

2009-11-06 15:54:15

WCF異步調用

2009-08-21 11:02:55

C#異步調用

2009-07-01 14:05:23

JavaScript異

2010-02-22 13:28:05

WCF異步調用

2009-08-21 11:24:16

C#異步調用

2011-03-02 08:57:22

jQueryJavaScript

2009-12-07 14:35:42

WCF異步調用

2012-10-29 10:59:27

Windows 8

2009-12-07 14:26:47

WCF異步調用
點贊
收藏

51CTO技術棧公眾號

日韩av网站大全| 一本精品一区二区三区| 丝瓜av网站精品一区二区| 精品盗摄一区二区三区| 97视频网站| 欧美激情亚洲| 欧美国产第二页| 国产黄色大片在线观看| 亚洲精品国产精华液| 国产又粗又大又爽的视频| 日韩影片在线观看| 亚洲欧美一区二区三区久久 | 国产伦精品一区二区三区视频孕妇 | 亚洲免费看片| 亚洲免费中文| 视频免费在线看| 久久青草久久| 久久久久久久久久久人体| 91av俱乐部| 加勒比久久高清| 亚洲国产精品久久一线不卡| 久久久久国产精品免费| 欧美亚州在线观看| 亚洲激情在线观看| 欧美成人一二三| 精品福利视频导航| 久久久精品一品道一区| 免费观看亚洲视频大全| 日韩毛片在线免费看| 日本一区二区三区免费观看| 亚洲一二三级电影| 福利在线小视频| 日韩中文字幕亚洲一区二区va在线| 国产日韩欧美在线播放| 成人18夜夜网深夜福利网| 在线精品国产欧美| 亚洲色图图片| 国内精品400部情侣激情| 免费看成人吃奶视频在线| 欧美一区二区大胆人体摄影专业网站| 91午夜精品| 国产成人免费91av在线| 欧美成人首页| 四虎影院一区二区三区| 国产成人在线视频网站| 777视频在线| jiyouzz国产精品久久| 高清精品在线| 97在线看免费观看视频在线观看| av网站大全免费| www.豆豆成人网.com| 精品欧美久久久| 中文字幕av在线| 欧美视频免费在线| 欧美美女黄色| 欧美疯狂性受xxxxx另类| 国产成人一二| 国产精品久久久久高潮| 国产精品人人爽人人做我的可爱| 午夜精品短视频| 91麻豆免费在线观看| 老司机色在线视频| 欧美午夜xxx| 欧美1级2级| 97欧美精品一区二区三区| 99成人超碰| 国产激情片在线观看| 中文无字幕一区二区三区| 精品乱码一区二区三四区视频 | 精品日韩在线观看| 粉嫩一区二区| 国产不卡视频在线| 视频一区在线播放| 国产男女爽爽爽| 成人日批视频| 91色中文字幕| 成人福利在线看| 国产福利91精品一区二区| 亚洲一区二区免费| 91精品国产66| 88在线观看91蜜桃国自产| 国产精品久久二区| 丁香婷婷综合色啪| 毛片一级免费一级| 成人xxx免费视频播放| 99porn视频在线| 免费一区二区视频| jizz国产| 青草热久免费精品视频| 久久久蜜桃精品| 欧美视频精品| 久久久久久网址| 国产亚洲综合av| 97视频一区| 国产乱子视频| 91久久国产自产拍夜夜嗨| 午夜av一区二区| 欧美疯狂party性派对| 中文字幕在线二区| 国产66精品久久久久999小说| 在线视频国内自拍亚洲视频| 欧美成人一区二免费视频软件| 免费观看又污又黄在线观看国产| 国产精品国产亚洲精品看不卡15| 自拍偷拍国产亚洲| 亚洲精品美女91| 91tv亚洲精品香蕉国产一区| 欧美在线观看成人| 91精品久久久久久久久中文字幕| 精品伦理精品一区| 日本一区免费视频| 亚洲伊人春色| 国产精品电影| 成人亚洲在线观看| 久久婷婷国产综合尤物精品| 久久久国产精品免费| 午夜国产精品一区| 首页综合国产亚洲丝袜| 久久97视频| 深夜在线视频| 在线看小视频| 国产黄视频在线| 久久综合福利| 精品国模在线视频| 精品少妇一区二区三区| 国产精品久久久久永久免费观看| 欧美日韩视频一区二区三区| 成人免费一区| 国产蜜臀一区二区打屁股调教| caoporen人人| 鲁一鲁一鲁一鲁一色| 国产精品免费网站| 不卡毛片在线看| 欧美韩国日本在线观看| jizz亚洲| 台湾av在线二三区观看| 香艳视频网站| 欧美视频第一区| 97超碰人人澡| 国产日本在线播放| 亚洲高潮无码久久| 亚洲天堂第一区| 亚洲精品国产suv一区88| 久久综合九色欧美狠狠| 国产日韩在线一区二区三区| 国产欧美日韩综合一区在线观看 | 九一免费在线观看| 欧美日韩另类综合| 国产精品区在线| 电影亚洲一区| 亚洲精选在线| 亚洲精品视频自拍| 久久亚洲精品中文字幕冲田杏梨| 91久久国产自产拍夜夜嗨| 日本三级黄色网址| 中文字幕日本一区二区| 久久久777| 91精品久久久久久久91蜜桃| 国产精品一区电影| 手机福利视频欧美| 国产成人3p视频免费观看| 国产精品美女一区二区在线观看| 日韩欧美一区视频| 国产成人精品视频在线观看| 老太脱裤让老头玩ⅹxxxx| www在线视频| 91精品国产91久久久久久密臀| 日韩亚洲欧美视频| av观看在线| 葵司免费一区二区三区四区五区| 日本久久精品电影| 国产欧美在线看| 黄页视频在线观看| 久久夜色电影| 中文字幕久久午夜不卡| 亚洲偷欧美偷国内偷| 4444在线观看| 亚洲一区二区三区中文字幕在线观看| 国产91精品一区二区| 成人午夜激情影院| 青青草原综合久久大伊人精品优势 | 啊啊啊啊啊啊啊视频在线播放| 亚洲在线免费观看| 国产精品999视频| 草草视频在线| 老司机亚洲精品| 日韩欧美电影在线| 日韩一区二区三区高清| 黄色网址在线免费观看| 在线资源免费观看| 激情av在线| 亚洲国产欧美国产第一区| 四虎成人精品永久免费av九九| 青草国产精品| 99久久久久久中文字幕一区| 亚洲激情五月| 狠狠色丁香婷综合久久| av电影一区二区| 欧美性生交大片免网| 亚洲影院理伦片|