dubbo學習過程、使用經驗分享及實現原理簡單介紹,dubbo經驗分享

1、前言

整理這篇文章差很少花了兩天半時間,請尊重勞動成果,如轉載請註明出處http://blog.csdn.net/hzzhoushaoyu/article/details/43273099git

2、什麼是dubbo

Dubbo是阿里巴巴提供的開源的SOA服務化治理的技術框架,聽說只是剖出來的一部分開源的,但一些基本的需求已經能夠知足的,並且擴展性也很是好(至今沒領悟到擴展性怎麼作到的),經過spring bean的方式管理配置及實例,較容易上手且對應用無侵入。更多介紹可戳http://alibaba.github.io/dubbo-doc-static/Home-zh.htm。github

3、如何使用dubbo

1.服務化應用基本框架

如上圖所示,一個抽象出來的基本框架,consumer和provider是框架中必然存在的,Registry作爲全局配置信息管理模塊,推薦生產環境使用Registry,可實時推送現存活的服務提供者,Monitor通常用於監控和統計RPC調用狀況、成功率、失敗率等狀況,讓開發及運維瞭解線上運行狀況。web

應用執行過程大體以下:redis

  • 服務提供者啓動,根據協議信息綁定到配置的IP和端口上,若是已有服務綁定過相同IP和端口的則跳過
  • 註冊服務信息至註冊中心
  • 客戶端啓動,根據接口和協議信息訂閱註冊中心中註冊的服務,註冊中心將存活的服務地址通知到客戶端,當有服務信息變動時客戶端能夠經過定時通知獲得變動信息
  • 在客戶端須要調用服務時,從內存中拿到上次通知的全部存活服務地址,根據路由信息和負載均衡機制選擇最終調用的服務地址,發起調用
  • 經過filter分別在客戶端發送請求前和服務端接收請求後,經過異步記錄一些須要的信息傳遞到monitor作監控或者統計

2.服務接口定義

通常單獨有一個jar包,維護服務接口定義、RPC參數類型、RPC返回類型、接口異常、接口用到的常量,該jar包中不處理任何業務邏輯。spring

好比命名api-0.1.jar,在api-0.1.jar中定義接口數據庫

 

public interface UserService
{
    public RpcResponseDto isValidUser(RpcAccountRequestDto requestDto) throws new RpcBusinessException, RpcSystemException;
}


並在api-0.1.jar中定義RpcResponseDto,RpcAccountRequestDto,RpcBusinessException,RpcSystemException。api

 

服務端經過引用該jar包實現接口並暴露服務,客戶端引用該jar包引用接口的代理實例。緩存

3.註冊中心

開源的dubbo已支持4種組件做爲註冊中心,咱們部門使用推薦的zookeeper作爲註冊中心,因爲就瓶頸來講不會出如今註冊中心,風險較低,未作特別的研究或比較。服務器

  • zookeeper,推薦集羣中部署奇數個節點,因爲zookeeper掛掉一半的機器集羣就不可用,因此部署4臺和3臺的集羣都是在掛掉2臺後集羣不可用
  • redis
  • multicast,廣播受到網絡結構的影響,通常本地不想搭註冊中心的話使用這種調用
  • dubbo簡易註冊中心

對於zookeeper客戶端,dubbo在2.2.0以後默認使用zkclient,2.3.0以後提供可選配置Curator,提到這個點的緣由主要是由於zkclient發現一些問題:①服務器在修改服務器時間後zkClient會拋出日誌錯誤之類的異常而後容器(咱們使用resin)掛掉了,也不能肯定就是zkClient的問題,接入dubbo以前無該問題②dubbo使用zkclient不傳入鏈接zookeeper等待超時時間,使用默認的Integer.MAX_VALUE,這樣在zookeeper連不上的狀況下不報錯也沒法啓動;目前咱們準備尋找其餘解決方案,好比使用curator試下,還沒正式投入。網絡

4.服務端

配置應用名

 

<dubbo:application name="test"/>


配置dubbo註解識別處理器,不指定包名的話會在spring bean中查找對應實例的類配置了dubbo註解的

 

 

<dubbo:annotation/>

 

配置註冊中心,經過group指定註冊中心分組,可經過register配置是否註冊到該註冊中心以及subscribe配置是否從該註冊中心訂閱

 

<dubbo:registry address="zookeeper://127.0.0.1:2181/" group="test"/>

配置服務協議,多網卡可經過IP指定綁定的IP地址,不指定或者指定非法IP的狀況下會綁定在0.0.0.0,使用Dubbo協議的服務會在初始化時創建長鏈接

 

 

<dubbo:protocol name="dubbo" port="20880" accesslog="d:/access.log"></dubbo:protocol>

經過xml配置文件配置服務暴露,首先要有個spring bean實例(不管是註解配置的仍是配置文件配置的),在下面ref中指定bean實例ID,做爲服務實現類

 

 

<dubbo:service interface="com.web.foo.service.FirstDubboService" ref="firstDubboServiceImpl" version="1.0"></dubbo:service>

經過註解方式配置服務暴露,Component是Spring bean註解,Service是dubbo的註解(不要和spring bean的service註解弄混),如前文所述,dubbo註解只會在spring bean中被識別

@Component
@Service(version="1.0")
public class FirstDubboServiceImpl implements FirstDubboService
{

	@Override
	public void sayHello(TestDto test)
	{
		System.out.println("Hello World!");
	}

}

 

5.客戶端

同服務端配置應用名、註解識別處理器和註冊中心。 
配置客戶端reference bean。客戶端跟服務端不一樣的是客戶端這邊沒有實際的實現類的,因此配置的dubbo:reference實際會生成一個spring bean實例,做爲代理處理Dubbo請求,而後其餘要調用處直接使用spring bean的方式使用這個實例便可。

 

xml配置文件配置方式,id即爲spring bean的id,以後不管是在spring配置中使用ref="firstDubboService"仍是經過@Autowired註解都OK

 

<dubbo:reference interface="com.web.foo.service.FirstDubboService"
		version="1.0" id="firstDubboService" ></dubbo:reference>

 

另外開發、測試環境可經過指定Url方式繞過註冊中心直連指定的服務地址,避免註冊中心中服務過多,啓動創建鏈接時間過長,如

 

<dubbo:reference interface="com.web.foo.service.FirstDubboService"
		version="1.0" id="firstDubboService" url="dubbo://127.0.0.1:20880/"></dubbo:reference>

 

註解配置方式引用,

 

@Component
public class Consumer
{
	@Reference(version="1.0")
	private FirstDubboService service;
	
	public void test()
	{
		TestDto test = new TestDto();
		test.setList(Arrays.asList(new String[]{"a", "b"}));
		test.setTest("t");
		service.sayHello(test);
	}
}

Reference被識別的條件是spring bean實例對應的當前類中的field,如上是直接修飾spring bean當前類中的屬性

 

這個地方看了下源碼,本應該支持當前類和父類中的public set方法,可是看起來是個BUG,Dubbo處理reference處部分源碼以下

 

Method[] methods = bean.getClass().getMethods();
        for (Method method : methods) {
            String name = method.getName();
            if (name.length() > 3 && name.startsWith("set")
                    && method.getParameterTypes().length == 1
                    && Modifier.isPublic(method.getModifiers())
                    && ! Modifier.isStatic(method.getModifiers())) {
                try {
                	Reference reference = method.getAnnotation(Reference.class);
                	if (reference != null) {
	                	Object value = refer(reference, method.getParameterTypes()[0]);
	                	if (value != null) {
	                		method.invoke(bean, new Object[] {  });//??這裏不是應該把value做爲參數調用麼,並且爲何上面if條件判斷參數爲1這裏不傳參數
	                	}
                	}
                } catch (Throwable e) {
                    logger.error("Failed to init remote service reference at method " + name + " in class " + bean.getClass().getName() + ", cause: " + e.getMessage(), e);
                }
            }
        }

 

6.監控中心

若是使用Dubbo自帶的監控中心,可經過簡單配置便可,先經過github得到dubbo-monitor的源碼,部署啓動後在應用配置以下

<dubbo:monitor protocol="registry" /> <!--經過註冊中心獲取monitor地址後創建鏈接-->

 

<dubbo:monitor address="dubbo://127.0.0.1:7070/com.alibaba.dubbo.monitor.MonitorService" /> <!--繞過註冊中心直連monitor,同consumer直連-->

 

7.服務路由

最重要輔助功能之一,可隨時配置路由規則調整客戶端調用策略,目前dubbo-admin中已提供基本路由規則的配置UI,到github下載源碼部署後很容易找到地方,這裏簡單介紹下怎麼用路由。 下面是dubbo-admin的新建路由界面,可配置信息都在圖片中有, 好比如今咱們有10.0.0.1~3三臺消費者和10.0.0.4~6三臺服務提供者,想讓1和2調用4,3調用5和6的話,則能夠配置兩個規則, 1.消費者IP:10.0.0.1,10.0.0.2 ;提供者IP:10.0.0.4 2.消費者IP:10.0.0.3;提供者IP:10.0.0.5,10.0.0.6 另外,IP地址支持結尾爲*匹配全部,如10.0.0.*或者10.0.*等。 不匹配的配置規則和匹配的配置規則是一致的。 
配置完成後可在消費者標籤頁查看路由結果 

8.負載均衡

dubbo提供4種負載均衡方式:

  • Random,隨機,按權重配置隨機機率,調用量越大分佈越均勻,默認是這種方式
  • RoundRobin,輪詢,按權重設置輪詢比例,若是存在比較慢的機器容易在這臺機器的請求阻塞較多
  • LeastActive,最少活躍調用數,不支持權重,只能根據自動識別的活躍數分配,不能靈活調配
  • ConsistentHash,一致性hash,對相同參數的請求路由到一個服務提供者上,若是有相似灰度發佈需求可採用

dubbo的負載均衡機制是在客戶端調用時經過內存中的服務方信息及配置的負責均衡策略選擇,若是對本身系統沒有一個全面認知,建議先採用random方式。

9.dubbo過濾器

有須要本身實現dubbo過濾器的,可關注以下步驟: 以下是dubbo rpc access log的過濾器,僅對服務提供方有效,且參數中須要帶accesslog,也就是配置protocol或者serivce時配置的accesslog="d:/rpc_access.log"

@Activate(group = Constants.PROVIDER, value = Constants.ACCESS_LOG_KEY)
public class AccessLogFilter implements Filter {
}

 

10.其餘特性

http://alibaba.github.io/dubbo-doc-static/User+Guide-zh.htm#UserGuide-zh-%3Cdubbo%3Amonitor%2F%3E

可關注以上連接內容,dubbo提供較多的輔助功能特性,大多目前咱們暫時未使用到,後續咱們這邊關注到的兩個特性可能會再引進來使用:

  • 結果緩存,免得本身再去寫一個緩存,對緩存沒有特殊要求的話直接使用dubbo的好了
  • 分組合並,對RPC接口不一樣的實現方式分別調用而後合併結果的一種調用模式,好比咱們要查用戶是否合法,一種咱們要查是否在黑名單,同時咱們還要關注登陸信息是否異常,而後合併結果

4、前車可鑑

這個主要是在整個學習及使用過程當中記錄的,以及一些同事在初識過程問過個人,這邊作了整理而後直接列舉在下面:

1.服務版本號

  • 引用只會找相應版本的服務

 

<dubbo:serviceinterface=「com.xxx.XxxService」 ref=「xxxService」 version=「1.0」 />
<dubbo:referenceid=「xxxService」 interface=「com.xxx.XxxService」 version=「1.0」/>

 

  • 爲了從此更換接口定義發佈在線時,可不停機發布,使用版本號

 

2.暴露一個內網一個外網IP問題

爲了在測試環境提供一個內網訪問的地址和一個辦公區訪問的地址。

•增長一個指定IP爲內網地址的服務協議 •增長一個不指定IP的服務協議,可是在/etc/hosts中hostname對應的IP要爲外網IP 
上面這種方案是一開始使用的方案,後面發現dubbo在啓動過程不管是否配路由仍是會一個個去鏈接,雖然不影響啓動,可是因爲存在超時因此會影響啓動時間,並且每臺機器還得特別配置指定IP,後面使用另一套方案:

 

 

使用這種方式須要注意作好防火牆控制等,好比在線默認也是不指定IP,會綁定在0.0.0.0,若是非法人員知道調用的外網IP和端口,並且能夠直接訪問就麻煩了(若是在應用中作IP攔截也成,須要注意有防範措施)。 

 

 

3.dubbo reference註解問題

前文介紹使用時已經提到過,@Reference只能在spring bean實例對應的當前類中使用,暫時沒法在父類使用;若是確實要在父類聲明一個引用,可經過配置文件配置dubbo:reference,而後在須要引用的地方跟引用spring bean同樣就行 

4.服務超時問題

目前若是存在超時,狀況基本都在以下幾點:

  • 客戶端耗時大,也就是超時異常時的client elapsed xxx,這個是從建立Future對象開始到使用channel發出請求的這段時間,中間沒有複雜操做,只要CPU沒問題基本不會出現大耗時,頂多1ms屬於正常
  • IOThread繁忙,默認狀況下,dubbo協議一個客戶端與一個服務提供者會創建一個共享長鏈接,若是某個客戶端處於特別繁忙併且一直往一個服務提供者塞請求,可能形成IOThread阻塞,通常很是特殊的狀況纔會出現
  • 服務端工做線程池中線程所有繁忙,接收消息後塞入隊列等待,若是等待時間比預想長會引發超時
  • 網絡抖動,若是上述狀況都排除了,還出如今請求發出後,服務接收請求前超過預想時間,只能歸類到網絡抖動了,須要SA一塊兒查看問題
  • 服務自身耗時大,這個須要應用自身作好耗時統計,當出現這種狀況的時候須要用數據來講明問題及規劃優化方案,建議採用緩存埋點的方式統計服務中各個執行階段的耗時狀況,最終若是超過預想時間則把緩存統計的耗時狀況打日誌,減小日誌量,且可以獲得更明確的信息

如今咱們應用使用過程當中發現兩種類型的耗時,一種咱們目前只能歸類到網絡抖動,後續須要找運維一塊兒關注這個問題,另一種是因爲一些歷史緣由,數據庫查詢容易發生抖動,總有一個時間點會忽然多出不少超時。 

5.服務保護

服務保護的原則上是避免發生相似雪崩效應,儘可能將異常控制在服務周圍,不要擴散開。 說到雪崩效應,還得提下dubbo自身的重試機制,默認3次,當失敗時會進行重試,這樣在某個時間點出現性能問題,而後調用方再連續重複調用,很容易引發雪崩,建議的話仍是很據業務狀況規劃好如何進行異常處理,什麼時候進行重試。 服務保護的話,目前咱們主要從如下幾個方面來實施,也不成熟,還在摸索:

  • 考慮服務的dubbo線程池類型(fix線程池的話考慮線程池大小)、數據庫鏈接池、dubbo鏈接數限制是否都合適 
  • 考慮服務超時時間和重試的關係,設置合適的值 
  • 必定時間內服務異常數較大,則可考慮使用failfast讓客戶端請求直接返回或者讓客戶端再也不請求 

經領導推薦,還在學習Release it,後續有其餘想法,再回頭來編輯。 

6.zkclient的問題

前文已經提到過zkclient有兩個問題,修改服務器時間會致使容器掛掉;dubbo使用zkclient沒有傳超時時間致使zookeeper沒法鏈接的時候,直接阻塞Integer.MAX_VALUE。 正在調研curator,目前只能說curator不會在沒法鏈接的時候直接阻塞。 另外zkclient和curator的jar包應該都是jdk1.6編譯的,因此係統還在jdk1.5如下的話沒法使用。 

7.註冊中心的分組group和服務的不一樣實現group

這兩個東西徹底不一樣的概念,使用的時候不要弄混了。 registry上能夠配置group,用於區分不一樣分組的註冊中心,好比在同一個註冊中心下,有一部分註冊信息是要給開發環境用的,有一部分註冊信息時要給測試環境用的,能夠分別用不一樣的group區分開,目前對這個理解還不透徹,大體就是用於區分不一樣環境。 service和reference上也能夠配置group,這個用於區分同一個接口的不一樣實現,只有在reference上指定與service相同的group纔會被發現,還有前文提到的分組合並結果也是用的這個。 

5、dubbo如何工做的

其實dubbo整個框架內容並不算大,仔細看的話可能最多兩天看完一遍,可是目前仍是沒領悟到怎麼作到的擴展性,學習深度還不夠~ 要學習dubbo源碼的話,必需要拿出官方高清大圖才行。 
這張圖看起來挺複雜的樣子,真正拆分以後對照源碼來看會發現很是清晰、簡單直觀。

1.如何跟進源碼

入口就是各類dubbo配置項的解析,<dubbo:xxx />都是spring namespace,能夠看到dubbo jar包下META-INF裏面的spring.handlers,自定義的spring namespace處理器。 對於spring不太熟的同窗能夠先了解下這個功能,入口都在這裏,解析成功後每一個<dubbo:xxx />配置項都對應一個spring實例。

2.服務提供者

首先把這張圖拆分紅三塊,首先是服務端剖去網絡傳輸模塊,也就是大圖中的右上角。 

這裏主要抽幾個主要的類,從服務初始化到接收消息的流程簡單說明下,有興趣的再對照源碼看下會比較清晰。

  • ServiceBean

繼承ServiceConfig,作爲服務配置管理和配置信息校驗,每個dubbo:service配置或者註解都會對應生成一個ServiceBean的實例,維護當前服務的配置信息,並把一些全局配置塞入到該服務配置中。 另外ServiceBean自己是一個InitializingBean,在afterPropertiesSet時經過配置信息引導服務綁定和註冊。 能夠留意到ServiceBean還實現了ApplicationListener,在所有spring bean加載完成後判斷是否延遲加載的邏輯。

  • ProtocolFilterWrapper

通過serviceBean引導後進入該類,這個地方注意下,Protocol使用的裝飾模式,葉子只有DubboProtocol和RegistryProtocol,在中間調用中會繞來繞去,並且registry會走一遍這個流程,而後在RegistryProtocol中暴露服務再走一遍,注意每一個類的做用,不要被繞昏了就行,第一次跟進代碼的時候沒留意就暈頭轉向的。 在這以前其實還有個ProtocolListenerWrapper,封裝監聽器,在服務暴露後通知到監聽器,沒有複雜邏輯,若是沒特殊需求能夠先繞過。 再來講ProtocolFIlterWrapper,這個類的做用就是串聯filter調用鏈,若是有看過struts或者spring mvc攔截器源碼的應該不會陌生。

  • RegistryProtocol

註冊中心協議,若是配置了註冊中心地址,每次服務暴露確定首先引導進入這個類中,若是沒有註冊中心鏈接則會先建立鏈接,而後再引導真正的服務協議暴露流程,會再走一次ProtocolFilterWrapper的流程(此次引導到的葉子是DubboProtocol)。 在服務暴露返回後,會再執行服務信息的註冊和訂閱操做。

  • DubboProtocol

這個類的export相對較簡單,就是引導服務bind server socket。 另外該類還提供了一個內部類,用於處理接收請求,就是下面要提到的ExchangeHandler。

  • DubboProtocol$ExchangeHandler

接收反序列化好的請求消息,而後根據請求信息找到執行鏈,將請求再丟入執行鏈,讓其最終執行到實現類再將執行結果返回即整個過程完成。 

3.客戶端

客戶端模塊與服務端模塊比較相似,只是恰好反過來,一個是暴露服務,一個是引用服務,而後客戶端多出路由和負載均衡。 

  • ReferenceBean

繼承ReferenceConfig,維護配置信息和配置信息的校驗,該功能與ServiceBean相似 其自己還實現了FactoryBean,做爲實例工廠,建立遠程調用代理類;並且若是不指定爲init的reference都是在首次getBean的時候調用到該factoryBean的getObject才進行初始化 另外實現了InitializingBean,在初始化過程當中引導配置信息初始化和構建init的代理實例

  • InvokerInvocationHandler

看到這個類名應該就知道是動態代理的handler,這裏做爲遠程調用代理類的處理器在客戶端調用接口時引導進入invoker調用鏈

  • ProtocolFIlterWrapper

與Service那邊的功能相似,構建調用鏈

  • RegistryProtocol

與service那邊相似,若是與註冊中心尚未鏈接則創建鏈接,以後註冊和訂閱,再根據配置的策略返回相應的clusterInvoker 比service那邊有個隱藏較深的邏輯須要留意的,就是訂閱過程,RegistryDirectory做爲訂閱監聽器,在訂閱完成後會通知到RegistryDirectory,而後會刷新invoker,進入引導至DubboProtocol的流程,與變動的service創建長鏈接,第一次發生訂閱時就會同步接收到通知並將已存在的service存到字典

  • DubboProtocol

在訂閱過程當中發現有service變動則會引導至這裏,與服務創建長鏈接,整個過程爲了獲得串聯執行鏈Invoker

  • ClusterInvoker

ClusterInvoker由RegistryProtocol構建完成後,內部封裝了Directory,在調用時會從Directory列舉存活的service對應的Invoker,Directory做爲被通知對象,在service有變動時也會及時獲得通知 調用時在集羣中發現存在多節點的話都會經過clusterInvoker來根據配置抉擇最終調用的節點,包括路由方式、負載均衡等 dubbo自己支持的節點調用策略包括好比failoverClusterInvoker在失敗時進行重試其餘節點,failfastClusterInvoker在失敗時返回異常,mergeableClusterInvoker則是對多個實現結果進行合併的等等不少

  • DubboInvoker

承接上層的調用信息,做爲調用結構的葉子,將信息傳遞到exchange層,主要用來和echange交互的功能模塊

4.網絡傳輸層

從exchange往下都是算網絡傳輸,包括作序列化、反序列化,使用Netty等IO框架發送接收消息等邏輯,先前看的時候沒有作統一梳理,後續有機會再來編輯吧。 

相關文章
相關標籤/搜索