dubbo非同步呼叫的bug

2021-09-07 05:20:34 字數 2110 閱讀 6213

現象

現有3個服務,關係如下,servicea非同步呼叫serviceb,serviceb同步呼叫servicec。其中serviceb暴露出的介面為非同步方式。表現的現象為,serviceb每次呼叫servicec時,第一次的返回結果為null,後面幾次呼叫時均能正常返回結果。

問題排查

專案中對於所有的dubbo呼叫均有記錄日誌,每次呼叫主要包含2條日誌,cs和cr日誌。cs為consumer send,開始呼叫時日誌。cr為接收到provider的應答日誌。 

通過觀察日誌發現。呼叫servicec的日誌有不合理的地方。在正常的同步呼叫中,如有多次呼叫,日誌現實順序為cs,cr,cs,cr。實際出現的日誌順序為cs,cs,cr,cr。初步懷疑第一次返回為null是由於非同步呼叫所致。 

通過dubbo admin檢視介面的配置資訊,發現引數均無異常。並且在servicec端的provider上觀察到每次呼叫均有返回資料。所以排除了servicec的provider的問題。 

再次編寫testcase單獨測試servicec服務,發現testcase返回資料均正常。也證實了servicec服務沒有問題。 

最後通過詢問是否有非同步配置時,發現serviceb暴露的介面是非同步方式。修改serviceb為同步方式,重新測試,發現問題解決。每次呼叫均正常接收到結果。調整serviceb為非同步,問題重現。 

最終確認了問題發生場景,即serviceb為非同步服務,在serviceb裡面同步呼叫servicec,最終表現為serivcec的呼叫是非同步,導致問題產生。

**分析

dubbo的provider和consumer均由invoker演變而來。並且都是abstractinvoker的實現類。 

abstractinvoker

mapcontext = rpccontext.getcontext().getattachments();

if (context != null)

// b點

if (geturl().getmethodparameter(invocation.getmethodname(), constants.async_key, false))

dubboinvoker

// c點

boolean isasync = rpcutils.isasync(geturl(), invocation);

....

if (isasync)

非同步呼叫serviceb時,此時b點的判斷為true,會在attachment中設定async的值為true,rpccontext中設定了async的值。

隨後呼叫serviceb的實際方法。serviceb呼叫servicec是,**在a點,會將rpccontext的值設定到當前的invocation中

由於rpccontext是採用threadlocal儲存的資料,並且在第一步時設定了async值,導致在2方法執行後,invocation中的async的值為true。

在進行servicec的遠端呼叫時,由於invocation中async值為true,導致c點判斷為非同步呼叫,在d點時new了乙個rpcresult。所以在第一次呼叫servicec時,返回結果為null。

第一次呼叫servicec結束時,在consumer的filter chain中,有乙個consumercontextfilter,在呼叫結束後會執行rpccontext.getcontext().clearattachments()方法,清除rpccontext中的資訊,也就清除了async標識。

第二次呼叫servicec時,由於rpccontext中沒有了async標識,判斷為同步呼叫,所以會正常返回結果。

解決方式

分析了問題產生的原因後,在不修改dubbo原始碼的情況,可以有一下幾種處理方式。 

1. 將serviceb改為同步呼叫,如果業務上確實需要非同步呼叫,有以下2種處理方式 

* serviceb的方法無需返回值,可採用oneway的方式 

* 有返回值,並且需要非同步,最簡單的方式為在實現中使用執行緒池執行業務。 

2. 增加乙個provider端的filter,保證在filter鏈的結尾,在執行方法前,清除attachment中的async標誌。也可達到同樣的效果。

或寫個dubbo介面的重新封裝,再加個springboot的async標識,**非同步。

Dubbo 同步 非同步呼叫的幾種方式

我們知道,dubbo 預設協議採用單一長連線,底層實現是 netty 的 nio 非同步通訊機制 基於這種機制,dubbo 實現了以下幾種呼叫方式 同步呼叫是一種阻塞式的呼叫方式,即 consumer 端 一直阻塞等待,直到 provider 端返回為止 通常,乙個典型的同步呼叫過程如下 consu...

dubbo非同步呼叫三種方式

非同步通訊對於服務端響應時間較長的方法是必須的,能夠有效地利用客戶端的資源,在dubbo中,消費端通過 async true 標識。具體有三種方式 1 nio future主動獲取結果,返回結果放在rpccontext中 需要注意的是,由於rpccontext是單例模式,所以每次呼叫完後,需要儲存乙...

同步呼叫與非同步呼叫

一 什麼是同步呼叫與非同步呼叫 定義 1 同步就是整個處理過程順序執行,當各個過程都執行完畢,並返回結果。2 非同步呼叫則是只是傳送了呼叫的指令,呼叫者無需等待被呼叫的方法完全執行完畢 而是繼續執行下面的流程。例如,在某個呼叫中,需要順序呼叫 a,b,c三個過程方法 如他們都是同步呼叫,則需要將他們...