Dubbo 2.7新特性之非同步化改造

来源:https://www.cnblogs.com/thisiswhy/archive/2019/12/10/12019070.html
-Advertisement-
Play Games

通過對比Dubbo2.6.0,2.7.0,2.7.3版本的源碼,分析Dubbo2.7 非同步化的改造的細節。請大家多多指教。 ...


 

這是why技術的第1篇原創文章

我與Dubbo的二三事

我是2016年畢業的,在我畢業之前,我在學校裡面學到的框架都是SSH,即struts+spring+hibernate,是的你沒有看錯,在大學裡面的課本裡面學的是strusts,這個還沒畢業就被基本拋棄的框架。然而我大四齣去實習,用的技術是SSM,即Spring,SpringMVC,Mybatis。實習的時候做的項目都是外包項目,非常傳統的單體大項目,和學校裡面做課程設計一樣,所有的功能包括前後端都糅合在一個項目裡面,根本不知道什麼是分散式架構,不誇張的說,那個時候我對分散式這一塊的知識無限趨近於零。

第一次接觸到分散式的概念是我正式參加工作後,第一家公司屬於一家互聯網公司,做第三方支付。我甚至現在還記得加入這個公司之後,第一次在同事的幫助下,分別把支付服務和賬務服務的Demo,兩個項目,在兩個IDEA中運行起來,然後我在賬務服務打了一個斷點,運行支付服務的測試用例,最後程式在賬務服務的斷點處停了下來!程式停下來的時候,我仿佛感覺看到了"神跡",顛覆了我前4年的大學學習中的固有印象!那個時候,我才知道了還有分散式這麼一回事,才第一次接觸到Dubbo,那個時候,程式猿的大門才向我徐徐打開,那個時候,我才知道,我一直在新手村待了4年。


Dubbo的坎坷一生

你的一生中總是會碰到幾個十分神秘的人,他們看起來或者仙風道骨,或者平平無奇,他們總是問你一些終極的問題,總是會引起你的思考,你也會無情的拒絕,因為你知道,這事,不靠譜,這些人就是“算命先生”,總是問你:"你從哪裡來?你往哪裡去?你60歲的時候會有一道坎,瞭解一下?"

我不知道我的前世,也無法預知自己今生還沒發生的坎,但是我知道Dubbo從哪裡來,往哪裡去,瞭解一下?

出生豪門:

2011 年 10 月 27 日,阿裡巴巴開源了自己服務化治理方案的核心框架 Dubbo,服務治理的設計理念開始逐漸在國內軟體行業中落地,並被廣泛應用。自開源後,許多非阿裡系公司選擇使用 Dubbo。

半路夭折:

2012 年 10 月 23 日,Dubbo 2.5.3 發佈後,在 Dubbo 開源將滿一周年之際,阿裡基本停止了對 Dubbo 的主要升級。

2012 年 10 月 23 日,Dubbo 2.5.3 發佈後,在 Dubbo 開源將滿一周年之際,阿裡基本停止了對 Dubbo 的主要升級。2013 年,2014 年,更新了 2 次 Dubbo 2.4 的維護版本,然後停止了所有維護工作。至此,Dubbo 對 Spring 的支持也停留在了 Spring 2.5.6 版本上。

同行續命:

阿裡停止維護和升級 Dubbo 期間,噹噹網開始維護自己的 Dubbo 分支版本 Dubbox,新增支持了新版本的 Spring,支持了 Rest 協議等,並對外開源了 Dubbox。同時,網易考拉也維護了自己的獨立分支 Dubbok,可惜並未對外開源。

起死回生:

2017 年 9 月 7 日,Dubbo 悄悄在 GitHub 發佈了 2.5.4 版本。隨後,又迅速發佈了 2.5.5、2.5.6、2.5.7 等版本。在 10 月舉行的雲棲大會上,阿裡宣佈 Dubbo 被列入集團重點維護開源項目,這也就意味著 Dubbo 起死回生,開始重新進入快車道。

回歸正統:

2018 年 1 月 8 日,Dubbo 2.6.0 版本發佈,新版本將之前噹噹網開源的 Dubbox 進行了合併,實現了 Dubbo 版本的統一整合。

走向巔峰:

2018年2月,阿裡巴巴宣佈將Dubbo捐獻給apache,進入apache孵化器。

2019 年 1 月,2.7.0 release 版本發佈,這個即將畢業的 apache 版本支持了豐富的新特性,全新的 Dubbo Ops 控制台。時至 5 月,Dubbo 來到了 2.7.2 版本,期間積極引入了新的特性,支持 consul,nacos,etcd 等註冊中心。

2019 年 5 月 21 號,經過了漫長的孵化期,Dubbo 迎來了畢業。成為Apache基金會頂級項目。

從Dubbo的歷程可以看出,Dubbo的一生是坎坷的一生,雖然半路夭折,但是最後還是走向了巔峰。不知道為什麼,這個時候我想起了馬雲爸爸說的一句話:

阿裡說:我對Dubbo沒有興趣。因為我最快樂的時候,是噹噹網,幫我續命的時候!

很有馬雲爸爸的氣質,一脈相承,厲害厲害!


Dubbo的非同步化改造

Dubbo2.7新特性包括但不限於如下幾點:

1.非同步化改造

2.三大中心改造

3.服務治理增強

本文主要分享Dubbo2.7新特征之一,非同步化改造相關的內容。

Dubbo的四種調用方式:

此圖是本文的核心,本文分享的內容基本上都是對於此圖深入到源碼解級別的解析:

1.oneway --- 有去無回

oneway 指的是客戶端發送消息後,不需要接受響應。對於那些不關心服務端響應的請求,比較適合使用 oneway 通信。但是請註意,返回值定義為void的並不是oneway的調用方式,void表示的程式上不需要關心返回值,但是對Dubbo框架而言,還是需要構建返回數據的。

仔細看oneway調用方式的圖,可以看出:從客戶端到服務端,只有req,沒有resp;所以客戶端不需要阻塞等待。

2.sync --- 同步調用

sync是最常用的通信方式,也是Dubbo預設的通信方法。

還是仔細看sync調用方式的圖,再想一想你自己寫的Dubbo應用,或者公司其他的Dubbo應用,是不是就是你們現在正在使用的通信方式。客服端發起req請求到A服務端,然後在設置的超時時間內,一直等待A伺服器的響應resp,這個時候,我們說客戶端處於阻塞的狀態。當A伺服器返回resp後,客戶端才會繼續運行。

3.future和callback --- 非同步調用

future 和 callback 都屬於非同步調用的範疇,我們放在一起討論。

繼續仔細看future和callback調用方式的圖,可以看出他們的區別是:在接收響應時,future.get() 會導致線程的阻塞;callback 通常會設置一個回調線程,當接收到服務端響應時,自動執行,不會對當前線程造成阻塞。

源碼之下無秘密

1.Dubbo 2.6.0中體現調用方式的關鍵代碼

有了前面的四種調用方式的簡單介紹鋪墊。我們深入到源碼中一探究竟:

上圖是Dubbo2.6.0版本DubboInvoke.doInvoke()方法的截圖,先看個全局的代碼。

其中的箭頭解釋一下:

箭頭1:表明這段代碼的版本,Dubbo2.6.0版本。

箭頭2:判斷調用方式是否是oneway模式,即有去無回調用。

箭頭3:判斷調用是否是否是async模式,即非同步調用。

箭頭4:既不是有去無回(oneway),也不是 非同步調用(async),那麼就是sync模式,即同步調用。

對於紅框中的代碼,放大如下:

接下來對關鍵代碼進行解讀:

1.首先,Dubbo是怎麼判斷調用方式是前面說的4種調用方式(對於Dubbo2.6.x來說,其實是3種,2.7.0之後才支持了callback的調用方式)的哪一種的呢?

可以看到這兩行代碼:

boolean isAsync = RpcUtils.isAsync(getUrl(), invocation); boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);

可以對著關鍵代碼解讀的圖來看,這兩行代碼的用途,就是判斷你的配置文件(註解的方式或者dubbo.xml)中有沒有配置async=true或者return=true。

2.接下來我們重點看一下我說的“最騷”的這一行代碼:


真的"騷"啊,當是非同步調用的時候,Dubbo把future放到RpcContext的上下文中,然後構造一個空的RpcResult給調用方,調用方再從上下文中把future取出來,需要用返回的值的時候調用一下future.get()方法。完成非同步調用的操作。

同步調用的時候,dubbo也有拿到了這個future,但是並沒有返回,而是直接調用了future.get()方法,這就是同步調用。

綜上:我認為同步調用和非同步調用的區別就是誰去調用future.get()方法。如果是Dubbo調用則是同步調用,如果是客戶端調用者是非同步方法。

2.Dubbo 2.7.0中體現調用方式的關鍵代碼

接下來,我們看一下Alibaba提供給Apache的初始版本,即2.7.0版本中體現調用方式的關鍵代碼。

朋友們可以先看左上角,確實是Dubbo2.7.0版本的代碼。然後紅框中圈起來的代碼,看起來和Dubbo2.6.0版本中的差不多,那我們就對比著看。

看到這個地方的時候我曾經走了一點彎路,甚至走上了歧途,一度質疑Dubbo的這個地方的源碼是有問題的,畢竟我們搞技術的,就是一個大膽假設,小心求證吧。所以我給Dubbo提了一個issus.如下:

這裡就不講我走上歧途的過程了,後面有機會再分享。大家可以去看看,會不會被固化思維給帶偏了。

這裡的兩個回答,第一個解答了我的問題,我看了後恍然大悟,這題屬於當局者迷旁觀者清。

第二個回答,建議我看一下最新版本的代碼,當時的最新版本的代碼是2.7.3。所以我把2.7.3版本的代碼拉了下來。

3.Dubbo 2.7.3中體現調用方式的關鍵代碼

接下來,我們就看看2.7.3中體現調用方式的關鍵代碼,請各位朋友坐穩扶好,這裡變化較大,車速較快,非常優秀。

 

首先我們可以看到isOneway的判斷還是我們熟悉的代碼。但是這裡只有一個if-else了。Dubbo調用有四種方式,if判斷了isOneway,那麼剩下的三種都在這個else裡面啦。

看到這裡,筆者冷靜的思考了一下,剩下的三種調用方式,sync調用,future調用,callback調用。其中sync調用是預設的方式,沒有在這個地方體現出來,那麼直覺告訴我在某個地方一定有一個非同步轉同步的調用。於是乎,我發現了這樣一個神奇的類:

AsyncToSyncInvoker方法中的54行asyncResult.get(),其中asyncResult繼承自Future,用源碼說話:

接著我們說說AsyncToSyncInvoker方法中的53行,getInvokeMode().

getInvokeMode()是RpcInvocation里InvokeMode的get方法。而且2.6.0裡面RpcInvocation是沒有invokeMode這個成員變數的。是2.7.0版本後新加的。

至此,基本圓滿了。感謝大神指引我看最新版本的代碼。

然後在上一個對比圖:

Show me the code

Dubbo 2.6.0的非同步化實現:

1.dubbo.xml配置,加入async="true"

<dubbo:reference id="asyncService" interface="org.apache.dubbo.demo.api.AsyncService" async="true"/>

2.dubbo介面定義:

public interface AsyncService{ String sayHello(String name);}

3.非同步調用,從RpcContext上下文中取出future,然後調用這個最"騷"的future.get()方法。還記得之前說的嘛:同步調用和非同步調用的區別就是誰去調用future.get()方法。這裡是客戶端調用,所以是非同步調用。

AsyncService.sayHello("hello");Future<String> fooFuture=RpcContext.getContext().getFuture();fooFuture.get();

有幾個弊端:

1.不太符合非同步編程的習慣,需要從一個上下文類中獲取到 Future

2.如果多個非同步調用,使用不當很容易造成上下文污染

3.Future 並不支持 callback 的調用方式

Dubbo 2.7.x的非同步化實現:

無需相關配置中進行特殊配置,顯示聲明非同步介面即可:

public interface AsyncService{ String sayHello(String name); default CompletableFuture<String> sayHiAsync(String name){ return CompletableFuture.completedFuture(sayHello(name)); }}

使用callback方式處理返回值

CompletableFuture<String> future = asyncService.sayHiAsync("hi");future.whenComplete((retValue, exception) -> { if (exception == null) { System.out.println(retValue); } else { exception.printStackTrace(); }});

那麼為什麼Dubbo2.7.0這樣簡單的幾行代碼就能實現非同步化了呢?記住,源碼之下無秘密:

完結撒花,關註我吧。下期再見,謝謝大家!

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 屬性 語法格式:修飾符 類型 屬性名 = 初值; 說明: 修飾符:public、protected、private:用於表示成員變數的訪問許可權。static:表示該成員變數為類變數,也稱為靜態變數。final:表示將該成員變數聲明為常量,其值無法更改。 類型:表示變數的類型。 屬性名:表示變數名稱。 ...
  • Java類的初始化順序 多說無益,以下是本人親自試驗的代碼,一目瞭然: 1 package test1; 2 3 public class Test { 4 public static void main(String[] argc) { 5 new Child(); 6 System.out.pr ...
  • composer install thinkphp6 報錯 Parse error: syntax error, unexpected ':', expecting '{' in vendor\topthink\think-helper\src\helper.php on line 233 ...
  • 前言本文的文字及圖片來源於網路,僅供學習、交流使用,不具有任何商業用途,版權歸原作者所有,如有問題請及時聯繫我們以作處理。作者:萬能搜吧 都是copy的百度SDK文檔,簡單說說怎麼用。 1、沒安裝Python的參見此文:Python學習筆記系列 1 ——安裝調試Python開發軟體 2、win+r輸 ...
  • import org.apache.spark.rdd.RDDimport org.apache.spark.{Partitioner, SparkConf, SparkContext} object Transformation { def main(args: Array[String]): U ...
  • AQS是JUC包中許多類的實現根基,這篇文章只是個人理解的產物,不免有誤,若閱讀過程中有發現不對的,希望幫忙指出[贊]! 1 AQS內臟圖 ​ 在開始瞭解 之前,我們先從上帝視角看看 是由幾個部分組成的。 ​ 內部維護了一個 修飾的 資源變數 ,裡面的所有操作都可以說跟這個變數有關係,因為它代表的就 ...
  • 出現如下錯誤: 解決辦法: DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'mxshop', 'HOST': '127.0.0.1', 'PORT': '3306', 'USER': 'mxshop' ...
  • 快速排序: 它的基本思想是:通過一躺排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一不部分的所有數據都要小, 然後再按次方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列。最壞情況的時 間複雜度為O(n2),最好情況時間複雜度為O(nlog ...
一周排行
    -Advertisement-
    Play Games
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 一、前言 這是一篇搭建許可權管理系統的系列文章。 隨著網路的發展,信息安全對應任何企業來說都越發的重要,而本系列文章將和大家一起一步一步搭建一個全新的許可權管理系統。 說明:由於搭建一個全新的項目過於繁瑣,所有作者將挑選核心代碼和核心思路進行分享。 二、技術選擇 三、開始設計 1、自主搭建vue前端和. ...
  • Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...