Dubbo Overview


Overview

Architecture

  1. Provider: 暴露服務的服務提供方。
  2. Consumer: 調用遠程服務的服務消費方。
  3. Registry: 服務注冊與發現的注冊中心。
  4. Monitor: 統計服務的調用次調和調用時間的監控中心。
  5. Container: 服務運行容器。

Relations

0. 服務容器負責啟動,加載,運行服務提供者。
1. 服務提供者在啟動時,向注冊中心注冊自己提供的服務。
2. 服務消費者在啟動時,向注冊中心訂閱自己所需的服務。
3. 注冊中心返回服務提供者地址列表給消費者,如果有變更,注冊中心將基於長連接推送變更數據給消費者。
4. 服務消費者,從提供者地址列表中,基於軟負載均衡算法,選一台提供者進行調用,如果調用失敗,再選另一台調用
5. 服務消費者和提供者,在內存中累計調用次數和調用時間,定時每分鍾發送一次統計數據到監控中心。
6. 注冊中心,服務提供者,服務消費者三者之間均為長連接,監控中心除外
7. 注冊中心通過長連接感知服務提供者的存在,服務提供者宕機,注冊中心將立即推送事件通知消費者
8. 注冊中心和監控中心全部宕機,不影響已運行的提供者和消費者,消費者在本地緩存了提供者列表

Invoker Procedures !important

0. 這里的Invoker是Provider的一個可調用Service的抽象,Invoker封裝了Provider地址及Service接口信息。
1. Directory代表多個Invoker,可以把它看成List<Invoker>,但與List不同的是,它的值可能是動態變化的,比如注冊中心推送變更。
2. Cluster將Directory中的多個Invoker偽裝成一個Invoker,對上層透明,偽裝過程包含了容錯邏輯,調用失敗后,重試另一個。
3. Router負責從多個Invoker中按路由規則選出子集,比如讀寫分離,應用隔離等。
4. LoadBalance負責從多個Invoker中選出具體的一個用於本次調用,選的過程包含了負載均衡算法,調用失敗后,需要重選。

Configuration Prorioty

Service

方法級優先,接口級次之,全局配置再次之。
如果級別一樣,則消費方優先,提供方次之。

Basic

JVM啟動-D參數優先,這樣可以使用戶在部署和啟動時進行參數重寫,比如在啟動時需改變協議的端口。
XML次之,如果在XML中有配置,則dubbo.properties中的相應配置項無效。
Properties最后,相當於缺省值,只有XML沒有配置時,dubbo.properties的相應配置項才會生效,通常用於共享公共配置,比如應用名。

Features

Check on Start

Dubbo缺省會在啟動時檢查依賴的服務是否可用,不可用時會拋出異常,阻止Spring初始化完成,以便上線時,能及早發現問題,默認check=true。

#如果你的Spring容器是懶加載的,或者通過API編程延遲引用服務,請關閉check,否則服務臨時不可用時,
#會拋出異常,拿到null引用,如果check=false,總是會返回引用,當服務恢復時,能自動連上。

可以通過check="false"關閉檢查,比如,測試時,有些服務不關心,或者出現了循環依賴,必須有一方先啟動。 

Fault Tolerance

Failover Cluster # DEFAULT
失敗自動切換,當出現失敗,重試其它服務器。(缺省)
通常用於讀操作,但重試會帶來更長延遲。
可通過retries="2"來設置重試次數(不含第一次)。

Failfast Cluster
快速失敗,只發起一次調用,失敗立即報錯。
通常用於非冪等性的寫操作,比如新增記錄。

Failsafe Cluster
失敗安全,出現異常時,直接忽略。
通常用於寫入審計日志等操作。

Failback Cluster
失敗自動恢復,后台記錄失敗請求,定時重發。
通常用於消息通知操作。

Forking Cluster
並行調用多個服務器,只要一個成功即返回。
通常用於實時性要求較高的讀操作,但需要浪費更多服務資源。
可通過forks="2"來設置最大並行數。 Broadcast Cluster 廣播調用所有提供者,逐個調用,任意一台報錯則報錯。(2.1.0開始支持) 通常用於通知所有提供者更新緩存或日志等本地資源信息。 

important 默認使用Failover,我們的系統使用這種方式,其實應該限定最大重試次數,比如兩次(因為一般一台失敗,其他的也會失敗)

#set max retry time 2
<dubbo:service retries="2" />
OR:
<dubbo:reference retries="2" /> OR: <dubbo:reference> <dubbo:method name="findFoo" retries="2" /> </dubbo:reference> 

Load Balancing

important 基於軟負載均衡算法

Random LoadBalance
隨機,按權重設置隨機概率。
在一個截面上碰撞的概率高,但調用量越大分布越均勻,而且按概率使用權重后也比較均勻,有利於動態調整提供者權重。

RoundRobin LoadBalance
輪循,按公約后的權重設置輪循比率。
存在慢的提供者累積請求問題,比如:第二台機器很慢,但沒掛,當請求調到第二台時就卡在那,久而久之,所有請求都卡在調到第二台上。

LeastActive LoadBalance
最少活躍調用數,相同活躍數的隨機,活躍數指調用前后計數差。
使慢的提供者收到更少請求,因為越慢的提供者的調用前后計數差會越大。

ConsistentHash LoadBalance
一致性Hash,相同參數的請求總是發到同一提供者。
當某一台提供者掛時,原本發往該提供者的請求,基於虛擬節點,平攤到其它提供者,不會引起劇烈變動。
算法參見:http://en.wikipedia.org/wiki/Consistent_hashing。
缺省只對第一個參數Hash,如果要修改,請配置<dubbo:parameter key="hash.arguments" value="0,1" /> 缺省用160份虛擬節點,如果要修改,請配置<dubbo:parameter key="hash.nodes" value="320" /> 

Thread Model

事件處理線程說明

0. 如果事件處理的邏輯能迅速完成,並且不會發起新的IO請求,比如只是在內存中記個標識,則直接在IO線程上處理更快,因為減少了線程池調度。
1. 但如果事件處理邏輯較慢,或者需要發起新的IO請求,比如需要查詢數據庫,則必須派發到線程池,否則IO線程阻塞,將導致不能接收其它請求。
2. 如果用IO線程處理事件,又在事件處理過程中發起新的IO請求,比如在連接事件中發起登錄請求,會報“可能引發死鎖”異常,但不會真死鎖。

Dispatcher #all default

0. all 所有消息都派發到線程池,包括請求,響應,連接事件,斷開事件,心跳等。
1. direct 所有消息都不派發到線程池,全部在IO線程上直接執行。
2. message 只有請求響應消息派發到線程池,其它連接斷開事件,心跳等消息,直接在IO線程上執行。
3. execution 只請求消息派發到線程池,不含響應,響應和其它連接斷開事件,心跳等消息,直接在IO線程上執行。
4. connection 在IO線程上,將連接斷開事件放入隊列,有序逐個執行,其它消息派發到線程池。

ThreadPool #fixed default

0. fixed 固定大小線程池,啟動時建立線程,不關閉,一直持有。(缺省)
1. cached 緩存線程池,空閑一分鍾自動刪除,需要時重建。
2. limited 可伸縮線程池,但池中的線程數只會增長不會收縮。(為避免收縮時突然來了大流量引起的性能問題)。

E.G

<dubbo:protocol name="dubbo" dispatcher="all" threadpool="fixed" threads="100" /> 

參數說明
iothread epoll模型,所以是CPU核數+1

<dubbo:protocol> threads int 可選 100 性能調優 服務線程池大小(固定大小) 2.0.5以上版本
<dubbo:protocol> iothreads int 可選 cpu個數+1 性能調優 io線程池大小(固定大小) 2.0.5以上版本

Multi-protocol

不同服務在性能上適用不同協議進行傳輸,比如大數據用短連接協議,小數據大並發用長連接協議。
我們系統使用dubbo協議,基於長連接的協議(傳輸大小有限制)。如果是大文件可以使用RMI

Multi-Registry,Multi-Group,Multi-Version,Group-Aggregation

見手冊,不過Group-Aggregation倒是很有意思

<dubbo:reference interface="com.xxx.MenuService" group="*"> <dubbo:method name="getMenuItems" merger="mymerge" /> </dubbo:service> #或:(指定合並方法,將調用返回結果的指定方法進行合並,合並方法的參數類型必須是返回結果類型本身) <dubbo:reference interface="com.xxx.MenuService" group="*"> <dubbo:method name="getMenuItems" merger=".addAll" /> </dubbo:service> 

JSR303 based validation

#interface
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>1.0.0.GA</version>
</dependency>

#implement
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>4.2.0.Final</version>
</dependency>

E.G

<dubbo:reference id="validationService" interface="com.alibaba.dubbo.examples.validation.api.ValidationService" validation="true" /> OR: <dubbo:service interface="com.alibaba.dubbo.examples.validation.api.ValidationService" ref="validationService" validation="true" /> 

Cache

結果緩存,用於加速熱門數據的訪問速度,Dubbo提供聲明式緩存,以減少用戶加緩存的工作量。

<dubbo:reference interface="com.foo.BarService"> <dubbo:method name="findBar" cache="lru" /> </dubbo:reference> 

泛話引用、泛話實現

使用Map代替POJO

回聲測試

所有服務自動實現EchoService接口,只需將任意服務引用強制轉型為EchoService,即可使用。

MemberService memberService = ctx.getBean("memberService"); // 遠程服務引用 EchoService echoService = (EchoService) memberService; // 強制轉型為EchoService String status = echoService.$echo("OK"); // 回聲測試可用性 

服務上下文與隱式傳參,AppContext 基於這個實現

架構

所有配置信息都將轉換為URL的參數,以字符串協議,類似於HTTP HEADER傳送。

RpcContext是一個ThreadLocal的臨時狀態記錄器,當接收到RPC請求,或發起RPC請求時,RpcContext的狀態都會變化。

RpcContext.getContext()

異步調用,本地調用based on injvm

流程圖

參數回調,本地存根

參數回調方式與調用本地callback或listener相同,只需要在Spring的配置文件中聲明哪個參數是callback類型即可,Dubbo將基於長連接生成反向代理,這樣就可以從服務器端調用客戶端邏輯。

不是把代碼傳過去

總的來說,沒有想到應用的場景

service:
<bean id="callbackService" class="com.callback.impl.CallbackServiceImpl" /> <dubbo:service interface="com.callback.CallbackService" ref="callbackService" connections="1" callbacks="1000"> <dubbo:method name="addListener"> <dubbo:argument index="1" callback="true" /> <!--也可以通過指定類型的方式--> <!--<dubbo:argument type="com.demo.CallbackListener" callback="true" />--> </dubbo:method> </dubbo:service> consumer: <dubbo:reference id="callbackService" interface="com.callback.CallbackService" /> 
callbackService.addListener("http://10.20.160.198/wiki/display/dubbo/foo.bar", new CallbackListener(){ public void changed(String msg) { System.out.println("callback1:" + msg); } }); 

延遲暴露

關於暴露時間

!important 在Spring解析到<dubbo:service />時,就已經向外暴露了服務,而Spring還在接着初始化其它Bean。

1. 強烈建議不要在服務的實現類中有applicationContext.getBean()的調用,全部采用IoC注入的方式使用Spring的Bean。
2. 如果實在要調getBean(),可以將Dubbo的配置放在Spring的最后加載。
3. 如果不想依賴配置順序,可以使用<dubbo:provider deplay=”-1” />,使Dubbo在Spring容器初始化完后,再暴露服務。
4. 如果大量使用getBean(),相當於已經把Spring退化為工廠模式在用,可以將Dubbo的服務隔離單獨的Spring容器。

Shutdown

服務提供方
停止時,先標記為不接收新請求,新請求過來時直接報錯,讓客戶端重試其它機器。
然后,檢測線程池中的線程是否正在運行,如果有,等待所有線程執行完成,除非超時,則強制關閉。

服務消費方
停止時,不再發起新的調用請求,所有新的調用在客戶端即報錯。
然后,檢測有沒有請求的響應還沒有返回,等待響應返回,除非超時,則強制關閉。

Dubbo是通過JDK的ShutdownHook來完成優雅停機的,所以如果用戶使用"kill -9 PID"等強制關閉指令,
是不會執行優雅停機的,只有通過"kill PID"時,才會執行。

我們的系統里面通過Kill -9 來執行

Container

服務容器是一個standalone的啟動程序,因為后台服務不需要Tomcat或JBoss等Web容器的功能,如果硬要用Web容器去加載服務提供方,增加復雜性,也浪費資源。
服務容器只是一個簡單的Main方法,並加載一個簡單的Spring容器,用於暴露服務

Spring Container
自動加載META-INF/spring目錄下的所有Spring配置。
配置:(配在java命令-D參數或者dubbo.properties中)
dubbo.spring.config=classpath*:META-INF/spring/*.xml ----配置spring配置加載位置

Jetty Container
啟動一個內嵌Jetty,用於匯報狀態。
配置:(配在java命令-D參數或者dubbo.properties中)
dubbo.jetty.port=8080 ----配置jetty啟動端口
dubbo.jetty.directory=/foo/bar ----配置可通過jetty直接訪問的目錄,用於存放靜態文件
dubbo.jetty.page=log,status,system ----配置顯示的頁面,缺省加載所有頁面

Log4j Container
自動配置log4j的配置,在多進程啟動時,自動給日志文件按進程分目錄。
配置:(配在java命令-D參數或者dubbo.properties中)
dubbo.log4j.file=/foo/bar.log ----配置日志文件路徑
dubbo.log4j.level=WARN ----配置日志級別
dubbo.log4j.subdirectory=20880 ----配置日志子目錄,用於多進程啟動,避免沖突

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM