天天看點

DUBBO配置規則詳解

研究DUBBO也已經大半年了,對它的大部分源碼進行了分析,以及對它的内部機制有了比較深入的了解,以及各個子產品的實作。DUBBO包含很多内容,如果想了解DUBBO第一步就是啟動它,進而可以很好的使用它,那麼如何更好的使用呢?就需要知道DUBBO的各個配置項,以及它可以通過哪些途徑進行配置。個人對配置的了解,就好比時對動物的馴服,如何很好的馴服一頭猛獸,那就需要知道它各種習性,進而調整,已達到自己期望的結果。這篇不對DUBBO有哪些配置項可以配置,但是通過這篇文章,你應該能夠知道DUBBO可以進行哪些配置。本文會通過分析DUBBO加載配置源碼的分析,來使得大家對DUBBO的配置一塊有更加深入的了解。進而達到“馴服”DUBBO,以使得它成為你們自己的DUBBO。

DUBBO在配置這一塊做的确實很完美,提供很很多參數,以及提供了多種管道。下面進入正題,看看DUBBO怎麼加載配置的。在講這些之前,先給大家介紹一下在DUBBO源碼層面定義了哪些類來存儲各個子產品的配置項,進而了解DUBBO可以對哪些子產品進行配置。

由于大部分項目都會使用Spring,而且DUBBO也提供了通過Spring來進行配置,那麼先從這裡進行着手。DUBBO加載Spring的內建時在dubbo-config下面的dubbo-config-spring子產品下面,其中有一個類<code>DubboNamespaceHandler</code>,它實作了Spring提供的接口<code>NamespaceHandlerSupport</code>。那麼Spring怎麼發現整個實作類的呢?在該子產品的META-INF檔案夾下有兩個檔案: spring.handlers和spring.schemas,這兩個檔案裡面制定了dubbo的namespace的XSD檔案的位置以及dubbo的namespace由<code>DubboNamespaceHandler</code>來處了解析。說了這麼多廢話,隻是想說明Spring是怎麼解析<code>&lt;dubbo:.../&gt;</code>配置的。

知道了DUBBO和Spring關于配置一塊時怎麼整合的之後,那麼你應該就不會詫異Spring怎麼那麼聰明,能夠解析dubbo的namespace。接下來看看<code>DubboNamespaceHandler</code>類裡面有什麼東西。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

可以看到再<code>init</code>方法裡面都是調用一個方法<code>registerBeanDefinitionParser</code>,但是參數略微有些不同。<code>registerBeanDefinitionParser</code>方法的第一個參數是dubbo的namespace下面節點名稱,第二個參數時該節點由誰來進行解析。例如:<code>registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));</code>是解析<code>&lt;dubbo:application../&gt;</code>配置資訊的,依此類推通過Spring可以配置<code>&lt;dubbo:module../&gt;</code>,<code>&lt;dubbo:registry../&gt;</code>等等,就不一一列舉了。至于每個标簽配置的作用,由于不是本篇的内容,是以這裡就不做過多的介紹。

通過上面應該清楚知道DUBBO可以配置哪些?這個問題應該不會再困擾你了。下面看看DUBBO是怎麼加載這些配置項的。

在項目中,會配置Spring的XML(雖然DUBBO也支援注解形式,但是個人不是很推崇,因為這樣會将DUBBO整合到你的項目源碼裡面,而我的建議是不要講DUBBO和你的項目綁的太緊,我的建議是DUBBO的配置和項目隔離,進而友善管理,加入以後項目不使用DUBBO,而使用其他的分布式RPC架構,對項目的調整會比較小),比如經常會通過下面的配置項引用一個遠端的服務:

通過上面的内容,應該知道<code>dubbo:reference</code>将會由<code>new DubboBeanDefinitionParser(ReferenceBean.class, false)</code>來解析(雖然看上去貌似所有配置都是用<code>DubboBeanDefinitionParser</code>來解析,但是有很大的不同)。那看看類<code>DubboBeanDefinitionParser</code>裡面做了什麼事情。

首先看類的定義,<code>DubboBeanDefinitionParser</code>實作了Spring的<code>BeanDefinitionParser</code>接口,該接口時專門用來解析Bean的定義的(一看類名就應該知道),并且實作了<code>public BeanDefinition parse(Element element, ParserContext parserContext)</code>方法(别看整個方法傳回了一個<code>BeanDefinition</code>對象,其實Spring并沒有利用整個傳回的對象,具體你可以看看Spring的源碼,是以要把Bean的定義注入到Spring容器中,就需要手動的往Spring中注入,因為Spring沒有給我們來做這件事請。),然後調用了靜态方法<code>private static BeanDefinition parse(...)</code>,那麼該類主要就是在靜态的方法<code>parse</code>上了。由于該方法内容實在是太長了,不便粘貼出全部内容,我隻分析主要的部分。在<code>DubboBeanDefinitionParser</code>構造方法參數上有一個<code>Class&lt;?&gt; beanClass</code>參數,它就是指定講目前标簽配置内容轉換成對應類的<code>BeanDefinition</code>并且注入到Spring容器中。

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

上面代碼很顯然看到是通過反射的形式擷取類的<code>get/set</code>方法然,進而判斷該參數是否可以通過Spring注入進去,最後添加到<code>beanDefinition</code>中,并且注入到Spring容器中。上面則是從Spring中加載配置的機制。

上面内容看到DUBBO可以對哪些子產品進行配置,并且通過哪些類來存儲這些配置資訊,例如:<code>ReferenceBean</code>,<code>RegistryConfig</code>,<code>ServiceBean</code>等,如果進去看看這些類的定義會發現他們都繼承了<code>AbstractConfig</code>抽象類,該抽象類中定義了<code>protected static void appendProperties(AbstractConfig config)</code>方法,參數時一個實作它自己的子類,接下來看看它做了什麼:

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

上面方法一開始是生産目前配置的字首<code>String prefix = "dubbo." + getTagName(config.getClass()) + "."</code>,此處可以看到dubbo的所有配置項都是以<code>dubbo.</code>開始的,接下來是通過<code>getTagName</code>方法生成目前配置類的配置名稱,進去看看<code>getTagName</code>是怎麼為各個配置類生成各自的配置名的。

分析上面的代碼很清楚的知道是怎麼生成各自配置類的配置名的,比如:<code>ReferenceBean</code>通過該方法會傳回<code>reference</code>。是以關于引用遠端Bean的配置都是以<code>dubbo.reference.</code>開頭的。生成目前配置類的字首之後,那麼還是按照管理反射出目前類的所有<code>set</code>方法,接下來就是從<code>System.getProperty</code>中取,如果其中沒有則會從<code>ConfigUtils.getProperty</code>中取。其中需要注意的是:從<code>System.getProperty</code>中取值并沒有判斷目前屬性是否有值(通過spring注入的),而從<code>ConfigUtils.getProperty</code>中取會判斷是否有值,如果沒有才會從其中取,這裡可以說明DUBBO配置項的優先級java -D優先于Spring配置,Spring配置優先于<code>properties</code>檔案的配置,這也符合一般項目的規則。不知道大家注意到沒,不管是<code>System.getProperty</code>還是<code>ConfigUtils.getProperty</code>都會取兩次,一次是<code>String pn = prefix + config.getId() + "." + property</code>,另一次是<code>String pn = prefix + property</code>,這兩種的差別就是多了一個<code>config.getId()</code>,可能會好奇<code>config.getId()</code>是什麼内容呢?在介紹Spring加載配置的時候,應該知道<code>config</code>對象其實是Spring容器裡面的,那麼這裡的<code>getId</code>,其實就是我們在配置Spring的Bean時候配置的ID。例如:

該配置會産生一個<code>ReferenceBean</code>對象,那麼此時的<code>config.getId()</code>就是<code>demoService</code>。是以<code>String pn = prefix + config.getId() + "." + property</code>配置的目的是有沒有針對目前某個Bean的配置項。比如:配置dubbo消費端的逾時時間,一般通過<code>dubbo.reference.timeout</code>,這其實是指定消費端所有Bean的逾時時間。有時候我們需要指定某個Bean逾時時間可以通過<code>dubbo.reference.{beanId}.timeout</code>來指定,例如上面的可以通過<code>dubbo.reference.demoService.timeout</code>來配置該Bean的逾時時間。

到此對DUBBO所有配置途徑以及其原理進行了分析和介紹,貌似還沒說怎麼取發現dubbo有哪些配置。這裡教大家怎麼去發現DUBBO有哪些配置。上面介紹過,DUBBO将配置項設定到各個配置類的實體中都是通過判斷是否有<code>get/set</code>方法,到這裡大家應該清楚怎麼去發現了吧?就是如果想了解dubbo某個子產品的配置,直接到對應的配置類中看它有哪些字段,知道它的字段名,以及确定你要配置的範圍,是全局還是某個bean,還有你想通過什麼途徑來配置,按照dubbo提供的規則很好确定對應的字段怎麼來進行配置。那麼你可能會說,字段時一個單詞知道是怎麼配置,那麼如果字段時多個單詞組合的呢?由于java的編碼風格一般時駝峰格式第:第一個單詞首字母小寫後面單詞首字母大寫,那麼這種情況對應dubbo來說怎麼擷取該配置項呢?看下面:

不難發現對于駝峰,dubbo時通過<code>-</code>字元來分割各個單詞。其實到這裡基本上可以很好的配置DUBBO了,但是有一個上面的途徑如果需要調整參數都需要重新開機應用,達不到動态調整,于是dubbo為了能夠滿足在項目不重新開機的情況下可以動态的調整配置參數。

這種方式主要原理就是通過管理器将動态參數釋出到注冊中心(zookeeper)中,然後各個節點可以獲得最新的配置變更,然後進行動态調整。想知道這一塊内容,需要取看看類<code>RegistryDirectory</code>的實作,該類它實作了<code>NotifyListener</code>。那麼它就可以監聽到注冊中心的任何變更。再了解<code>RegistryDirectory</code>之前,先看看DUBBO對每個服務在注冊中心存儲了哪些資訊?如果用的時zookeeper,那麼你會在每個服務節點目錄下面看到一下幾個目錄<code>consumers</code>,<code>providers</code>,<code>configurators</code>,<code>routers</code>。其中動态配置時放在<code>configurators</code>節點目錄下。服務消費端會監聽<code>configurators</code>目錄變更,如果變更則會調用<code>RegistryDirectory</code>的<code>void notify(List&lt;URL&gt; urls)</code>方法。監聽<code>configurators</code>目錄變更觸發的<code>void notify(List&lt;URL&gt; urls)</code>方法時,<code>urls</code>的是類似<code>override://...</code>,表示将覆寫調用該服務的某些配置(dubbo中對所有的調用配置都是通過URL的形式來展示的),講這些URL上面的參數資訊替換到調用服務端的URL上面取,并且重新構造該服務的<code>Invoke</code>對象,進而達到更新參數的目的。

整個場景在實際項目中是存在的,比如一個接口存在多個方法,有時候講參數配置現在再接口層面還是不夠的,需要精确到方法級别,例如對接口調用的逾時時間設定。dubbo對與方法級别的配置提供了兩種途徑。

通過多嵌套一個<code>dubbo:method</code>标簽來實作。

在DUBBO的管理器提供了動态配置的功能,通過添加動态配置,以及指定通知的消費端來指定某個服務的某個方法調整參數。它那裡配置的規則時方法名加配置項的名稱,例如:key配置成:<code>sayHello.timeout</code>,value=”10000”。那麼管理器會在注冊中心的對應服務的<code>configurators</code>添加一條<code>override://...?sayHello.timeout=10000</code>節點,指定消費端會監聽到這個變更,執行上面内容的流程。

到此關于DUBBO配置已經介紹完畢,可能有一些地方還時沒說明清楚,存在疑慮。如果有疑問歡迎提問或者自己取看看相關的DUBBO源碼,那一定會了解更加清楚。

繼續閱讀