整理這篇文章差很少花了兩天半時間,請尊重勞動成果,如轉載請註明出處http://blog.csdn.net/hzzhoushaoyu/article/details/43273099git
Dubbo是阿里巴巴提供的開源的SOA服務化治理的技術框架,聽說只是剖出來的一部分開源的,但一些基本的需求已經能夠知足的,並且擴展性也很是好(至今沒領悟到擴展性怎麼作到的),經過spring bean的方式管理配置及實例,較容易上手且對應用無侵入。更多介紹可戳http://alibaba.github.io/dubbo-doc-static/Home-zh.htm。github
如上圖所示,一個抽象出來的基本框架,consumer和provider是框架中必然存在的,Registry作爲全局配置信息管理模塊,推薦生產環境使用Registry,可實時推送現存活的服務提供者,Monitor通常用於監控和統計RPC調用狀況、成功率、失敗率等狀況,讓開發及運維瞭解線上運行狀況。web
應用執行過程大體以下:redis
通常單獨有一個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包引用接口的代理實例。緩存
開源的dubbo已支持4種組件做爲註冊中心,咱們部門使用推薦的zookeeper作爲註冊中心,因爲就瓶頸來講不會出如今註冊中心,風險較低,未作特別的研究或比較。服務器
對於zookeeper客戶端,dubbo在2.2.0以後默認使用zkclient,2.3.0以後提供可選配置Curator,提到這個點的緣由主要是由於zkclient發現一些問題:①服務器在修改服務器時間後zkClient會拋出日誌錯誤之類的異常而後容器(咱們使用resin)掛掉了,也不能肯定就是zkClient的問題,接入dubbo以前無該問題②dubbo使用zkclient不傳入鏈接zookeeper等待超時時間,使用默認的Integer.MAX_VALUE,這樣在zookeeper連不上的狀況下不報錯也沒法啓動;目前咱們準備尋找其餘解決方案,好比使用curator試下,還沒正式投入。網絡
配置應用名
<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!"); } }
同服務端配置應用名、註解識別處理器和註冊中心。
配置客戶端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); } } }
若是使用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直連-->
最重要輔助功能之一,可隨時配置路由規則調整客戶端調用策略,目前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.*等。 不匹配的配置規則和匹配的配置規則是一致的。
配置完成後可在消費者標籤頁查看路由結果
dubbo提供4種負載均衡方式:
dubbo的負載均衡機制是在客戶端調用時經過內存中的服務方信息及配置的負責均衡策略選擇,若是對本身系統沒有一個全面認知,建議先採用random方式。
有須要本身實現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 { }
http://alibaba.github.io/dubbo-doc-static/User+Guide-zh.htm#UserGuide-zh-%3Cdubbo%3Amonitor%2F%3E
可關注以上連接內容,dubbo提供較多的輔助功能特性,大多目前咱們暫時未使用到,後續咱們這邊關注到的兩個特性可能會再引進來使用:
這個主要是在整個學習及使用過程當中記錄的,以及一些同事在初識過程問過個人,這邊作了整理而後直接列舉在下面:
<dubbo:serviceinterface=「com.xxx.XxxService」 ref=「xxxService」 version=「1.0」 /> <dubbo:referenceid=「xxxService」 interface=「com.xxx.XxxService」 version=「1.0」/>
爲了在測試環境提供一個內網訪問的地址和一個辦公區訪問的地址。
•增長一個指定IP爲內網地址的服務協議 •增長一個不指定IP的服務協議,可是在/etc/hosts中hostname對應的IP要爲外網IP
上面這種方案是一開始使用的方案,後面發現dubbo在啓動過程不管是否配路由仍是會一個個去鏈接,雖然不影響啓動,可是因爲存在超時因此會影響啓動時間,並且每臺機器還得特別配置指定IP,後面使用另一套方案:
使用這種方式須要注意作好防火牆控制等,好比在線默認也是不指定IP,會綁定在0.0.0.0,若是非法人員知道調用的外網IP和端口,並且能夠直接訪問就麻煩了(若是在應用中作IP攔截也成,須要注意有防範措施)。
前文介紹使用時已經提到過,@Reference只能在spring bean實例對應的當前類中使用,暫時沒法在父類使用;若是確實要在父類聲明一個引用,可經過配置文件配置dubbo:reference,而後在須要引用的地方跟引用spring bean同樣就行
目前若是存在超時,狀況基本都在以下幾點:
如今咱們應用使用過程當中發現兩種類型的耗時,一種咱們目前只能歸類到網絡抖動,後續須要找運維一塊兒關注這個問題,另一種是因爲一些歷史緣由,數據庫查詢容易發生抖動,總有一個時間點會忽然多出不少超時。
服務保護的原則上是避免發生相似雪崩效應,儘可能將異常控制在服務周圍,不要擴散開。 說到雪崩效應,還得提下dubbo自身的重試機制,默認3次,當失敗時會進行重試,這樣在某個時間點出現性能問題,而後調用方再連續重複調用,很容易引發雪崩,建議的話仍是很據業務狀況規劃好如何進行異常處理,什麼時候進行重試。 服務保護的話,目前咱們主要從如下幾個方面來實施,也不成熟,還在摸索:
經領導推薦,還在學習Release it,後續有其餘想法,再回頭來編輯。
前文已經提到過zkclient有兩個問題,修改服務器時間會致使容器掛掉;dubbo使用zkclient沒有傳超時時間致使zookeeper沒法鏈接的時候,直接阻塞Integer.MAX_VALUE。 正在調研curator,目前只能說curator不會在沒法鏈接的時候直接阻塞。 另外zkclient和curator的jar包應該都是jdk1.6編譯的,因此係統還在jdk1.5如下的話沒法使用。
這兩個東西徹底不一樣的概念,使用的時候不要弄混了。 registry上能夠配置group,用於區分不一樣分組的註冊中心,好比在同一個註冊中心下,有一部分註冊信息是要給開發環境用的,有一部分註冊信息時要給測試環境用的,能夠分別用不一樣的group區分開,目前對這個理解還不透徹,大體就是用於區分不一樣環境。 service和reference上也能夠配置group,這個用於區分同一個接口的不一樣實現,只有在reference上指定與service相同的group纔會被發現,還有前文提到的分組合並結果也是用的這個。
其實dubbo整個框架內容並不算大,仔細看的話可能最多兩天看完一遍,可是目前仍是沒領悟到怎麼作到的擴展性,學習深度還不夠~ 要學習dubbo源碼的話,必需要拿出官方高清大圖才行。
這張圖看起來挺複雜的樣子,真正拆分以後對照源碼來看會發現很是清晰、簡單直觀。
入口就是各類dubbo配置項的解析,<dubbo:xxx />都是spring namespace,能夠看到dubbo jar包下META-INF裏面的spring.handlers,自定義的spring namespace處理器。 對於spring不太熟的同窗能夠先了解下這個功能,入口都在這裏,解析成功後每一個<dubbo:xxx />配置項都對應一個spring實例。
首先把這張圖拆分紅三塊,首先是服務端剖去網絡傳輸模塊,也就是大圖中的右上角。
這裏主要抽幾個主要的類,從服務初始化到接收消息的流程簡單說明下,有興趣的再對照源碼看下會比較清晰。
繼承ServiceConfig,作爲服務配置管理和配置信息校驗,每個dubbo:service配置或者註解都會對應生成一個ServiceBean的實例,維護當前服務的配置信息,並把一些全局配置塞入到該服務配置中。 另外ServiceBean自己是一個InitializingBean,在afterPropertiesSet時經過配置信息引導服務綁定和註冊。 能夠留意到ServiceBean還實現了ApplicationListener,在所有spring bean加載完成後判斷是否延遲加載的邏輯。
通過serviceBean引導後進入該類,這個地方注意下,Protocol使用的裝飾模式,葉子只有DubboProtocol和RegistryProtocol,在中間調用中會繞來繞去,並且registry會走一遍這個流程,而後在RegistryProtocol中暴露服務再走一遍,注意每一個類的做用,不要被繞昏了就行,第一次跟進代碼的時候沒留意就暈頭轉向的。 在這以前其實還有個ProtocolListenerWrapper,封裝監聽器,在服務暴露後通知到監聽器,沒有複雜邏輯,若是沒特殊需求能夠先繞過。 再來講ProtocolFIlterWrapper,這個類的做用就是串聯filter調用鏈,若是有看過struts或者spring mvc攔截器源碼的應該不會陌生。
註冊中心協議,若是配置了註冊中心地址,每次服務暴露確定首先引導進入這個類中,若是沒有註冊中心鏈接則會先建立鏈接,而後再引導真正的服務協議暴露流程,會再走一次ProtocolFilterWrapper的流程(此次引導到的葉子是DubboProtocol)。 在服務暴露返回後,會再執行服務信息的註冊和訂閱操做。
這個類的export相對較簡單,就是引導服務bind server socket。 另外該類還提供了一個內部類,用於處理接收請求,就是下面要提到的ExchangeHandler。
接收反序列化好的請求消息,而後根據請求信息找到執行鏈,將請求再丟入執行鏈,讓其最終執行到實現類再將執行結果返回即整個過程完成。
客戶端模塊與服務端模塊比較相似,只是恰好反過來,一個是暴露服務,一個是引用服務,而後客戶端多出路由和負載均衡。
繼承ReferenceConfig,維護配置信息和配置信息的校驗,該功能與ServiceBean相似 其自己還實現了FactoryBean,做爲實例工廠,建立遠程調用代理類;並且若是不指定爲init的reference都是在首次getBean的時候調用到該factoryBean的getObject才進行初始化 另外實現了InitializingBean,在初始化過程當中引導配置信息初始化和構建init的代理實例
看到這個類名應該就知道是動態代理的handler,這裏做爲遠程調用代理類的處理器在客戶端調用接口時引導進入invoker調用鏈
與Service那邊的功能相似,構建調用鏈
與service那邊相似,若是與註冊中心尚未鏈接則創建鏈接,以後註冊和訂閱,再根據配置的策略返回相應的clusterInvoker 比service那邊有個隱藏較深的邏輯須要留意的,就是訂閱過程,RegistryDirectory做爲訂閱監聽器,在訂閱完成後會通知到RegistryDirectory,而後會刷新invoker,進入引導至DubboProtocol的流程,與變動的service創建長鏈接,第一次發生訂閱時就會同步接收到通知並將已存在的service存到字典
在訂閱過程當中發現有service變動則會引導至這裏,與服務創建長鏈接,整個過程爲了獲得串聯執行鏈Invoker
ClusterInvoker由RegistryProtocol構建完成後,內部封裝了Directory,在調用時會從Directory列舉存活的service對應的Invoker,Directory做爲被通知對象,在service有變動時也會及時獲得通知 調用時在集羣中發現存在多節點的話都會經過clusterInvoker來根據配置抉擇最終調用的節點,包括路由方式、負載均衡等 dubbo自己支持的節點調用策略包括好比failoverClusterInvoker在失敗時進行重試其餘節點,failfastClusterInvoker在失敗時返回異常,mergeableClusterInvoker則是對多個實現結果進行合併的等等不少
承接上層的調用信息,做爲調用結構的葉子,將信息傳遞到exchange層,主要用來和echange交互的功能模塊
從exchange往下都是算網絡傳輸,包括作序列化、反序列化,使用Netty等IO框架發送接收消息等邏輯,先前看的時候沒有作統一梳理,後續有機會再來編輯吧。