1. 程式人生 > >RPC框架Dubbo深入分析

RPC框架Dubbo深入分析

轉換 緩存 並發 ont codec fin 由於 enc junit

1,背景

隨著互聯網的發展,網站應用的規模不斷擴大,常規的垂直應用架構已無法應對,分布式服務架構以及流動計算架構勢在必行,亟需一個治理系統確保架構有條不紊的演進

單一應用架構

當網站流量很小時,只需一個應用,將所有功能都部署在一起,以減少部署節點和成本

此時,用於簡化增刪改查工作量的?數據訪問框架(ORM)?是關鍵

垂直應用架構?當訪問量逐漸增大,單一應用增加機器帶來的加速度越來越小,將應用拆成互不相幹的幾個應用,以提升效率此時,用於加速前端頁面開發的?Web框架(MVC)?是關鍵分布式服務架構?當垂直應用越來越多,應用之間交互不可避免,將核心業務抽取出來,作為獨立的服務,逐漸形成穩定的服務中心,使前端應用能更快速的響應多變的市場需求此時,用於提高業務復用及整合的?分布式服務框架(RPC)?是關鍵分布式服務RPC框架

按業務線拆分

部署分離

每次發布只部署部分服務器

每個節點可根據不同需求伸縮擴展

每個應用之間更新,部署,運行不影響

團隊分離

數據分離

停止RPC濫用,垂直業務內優先通過本地jar調用,跨業務才采用RPC調用

正確的識別業務邏輯的歸屬,讓各個模塊最大化內聚,從性能,可用性和維護性上減少耦合

流動計算架構?當服務越來越多,容量的評估,小服務資源的浪費等問題逐漸顯現,此時需增加一個調度中心基於訪問壓力實時管理集群容量,提高集群利用率此時,用於提高機器利用率的資源調度和治理中心(SOA)是關鍵

2,需求

在大規模服務化之前,應用可能只是通過RMI或Hessian等工具,簡單的暴露和引用遠程服務,通過配置服務的URL地址進行調用,通過F5等硬件進行負載均衡

當服務越來越多時,服務URL配置管理變得非常困難,F5硬件負載均衡器的單點壓力也越來越大

此時需要一個服務註冊中心,動態的註冊和發現服務,使服務的位置透明

並通過在消費方獲取服務提供方地址列表,實現軟負載均衡和Failover,降低對F5硬件負載均衡器的依賴,也能減少部分成本

當進一步發展,服務間依賴關系變得錯蹤復雜,甚至分不清哪個應用要在哪個應用之前啟動,架構師都不能完整的描述應用的架構關系

這時,需要自動畫出應用間的依賴關系圖,以幫助架構師理清理關系

接著,服務的調用量越來越大,服務的容量問題就暴露出來,這個服務需要多少機器支撐?什麽時候該加機器??為了解決這些問題,第一步,要將服務現在每天的調用量,響應時間,都統計出來,作為容量規劃的參考指標其次,要可以動態調整權重,在線上,將某臺機器的權重一直加大,並在加大的過程中記錄響應時間的變化,直到響應時間到達閥值,記錄此時的訪問量,再以此訪問量乘以機器數反推總容量

3,Dubbo架構

Dubbo分層

層次結構

Business

Service

RPC

Config

Proxy

Registry

Cluster

Monitor

Protocol

Remoting

Exchange

Transport

Serialize

層說明

config(配置層 )

對外配置接口

以ServiceConfig,?ReferenceConfig為中心,可以直接new配置類,也可以通過spring解析配置生成配置類

proxy(服務代理層)

服務接口透明代理,生成服務的客戶端Stub和服務器端Skeleton

以ServiceProxy為中心,擴展接口為ProxyFactory

選擇

Javassist?ProxyFactory

Jdk?ProxyFactory

registry( 註冊中心層)

封裝服務地址的註冊與發現

以服務URL為中心,擴展接口為RegistryFactory,?Registry,?RegistryService

選擇

Zookeeper

支持基於網絡的集群方式,有廣泛周邊開源產品,建議使用dubbo-2.3.3以上版本(推薦使用)

依賴於Zookeeper的穩定性

Redis

支持基於客戶端雙寫的集群方式,性能高

要求服務器時間同步,用於檢查心跳過期臟數據

Multicast

去中心化,不需要安裝註冊中心

依賴於網絡拓普和路由,跨機房有風險

Simple

Dogfooding,註冊中心本身也是一個標準的RPC服務

沒有集群支持,可能單點故障

cluster( 路由層)

封裝多個提供者的路由及負載均衡,並橋接註冊中心

以Invoker為中心,擴展接口為Cluster,?Directory,?Router,?LoadBalance

Cluster選擇

Failover

失敗自動切換,當出現失敗,重試其它服務器,通常用於讀操作(推薦使用)

重試會帶來更長延遲

Failfast

快速失敗,只發起一次調用,失敗立即報錯,通常用於非冪等性的寫操作

如果有機器正在重啟,可能會出現調用失敗

Failsafe

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

調用信息丟失

Failback

失敗自動恢復,後臺記錄失敗請求,定時重發,通常用於消息通知操作

不可靠,重啟丟失

Forking

並行調用多個服務器,只要一個成功即返回,通常用於實時性要求較高的讀操作

需要浪費更多服務資源

Broadcast

廣播調用所有提供者,逐個調用,任意一臺報錯則報錯,通常用於更新提供方本地狀態

速度慢,任意一臺報錯則報錯

Router選擇

Random

隨機,按權重設置隨機概率(推薦使用)

在一個截面上碰撞的概率高,重試時,可能出現瞬間壓力不均

RoundRobin

輪循,按公約後的權重設置輪循比率

存在慢的機器累積請求問題,極端情況可能產生雪崩

LeastActive

最少活躍調用數,相同活躍數的隨機,活躍數指調用前後計數差,使慢的機器收到更少請求

不支持權重,在容量規劃時,不能通過權重把壓力導向一臺機器壓測容量

ConsistentHash

一致性Hash,相同參數的請求總是發到同一提供者,當某一臺提供者掛時,原本發往該提供者的請求,基於虛擬節點,平攤到其它提供者,不會引起劇烈變動

壓力分攤不均

路由規則

條件路由

基於條件表達式的路由規則,功能簡單易用

有些復雜多分支條件情況,規則很難描述

腳本路由

基於腳本引擎的路由規則,功能強大

沒有運行沙箱,腳本能力過於強大,可能成為後門

容器

Spring

自動加載META-INF/spring目錄下的所有Spring配置

Jetty

啟動一個內嵌Jetty,用於匯報狀態

大量訪問頁面時,會影響服務器的線程和內存

Log4j

自動配置log4j的配置,在多進程啟動時,自動給日誌文件按進程分目錄

用戶不能控制log4j的配置,不靈活

monitor( 監控層)

RPC調用次數和調用時間監控

以Statistics為中心,擴展接口為MonitorFactory,?Monitor,?MonitorService

protocol( 遠程調用層)

封裝RPC調用

以Invocation,?Result為中心,擴展接口為Protocol,?Invoker,?Exporter

選擇

Dubbo協議

采用NIO復用單一長連接,並使用線程池並發處理請求,減少握手和加大並發效率,性能較好(推薦使用)

適合於小數據量大並發的服務調用,以及服務消費者機器數遠大於服務提供者機器數的情況

Dubbo缺省協議不適合傳送大數據量的服務,比如傳文件,傳視頻等,除非請求量很低

Dubbo協議缺省每服務每提供者每消費者使用單一長連接,如果數據量較大,可以使用多個連接

為防止被大量連接撐掛,可在服務提供方限制大接收連接數,以實現服務提供方自我保護

在大文件傳輸時,單一連接會成為瓶頸

總結

連接個數:單連接

連接方式:長連接

傳輸協議:TCP

傳輸方式:NIO異步傳輸

序列化:Hessian二進制序列化

適用範圍:傳入傳出參數數據包較小(建議小於100K),消費者比提供者個數多,單一消費者無法壓滿提供者,盡量不要用dubbo協議傳輸大文件或超大字符串。

適用場景:常規遠程服務方法調用

Rmi協議

可與原生RMI互操作,基於TCP協議

偶爾會連接失敗,需重建Stub

Hessian協議

可與原生Hessian互操作,基於HTTP協議

需hessian.jar支持,http短連接的開銷大

Hessian協議用於集成Hessian的服務,Hessian底層采用Http通訊,采用Servlet暴露服務,Dubbo缺省內嵌Jetty作為服務器實現

可以和原生Hessian服務互操作

提供者用Dubbo的Hessian協議暴露服務,消費者直接用標準Hessian接口調用

或者提供方用標準Hessian暴露服務,消費方用Dubbo的Hessian協議調用

基於Hessian的遠程調用協議

總結

連接個數:多連接

連接方式:短連接

傳輸協議:HTTP

傳輸方式:同步傳輸

序列化:Hessian二進制序列化

適用範圍:傳入傳出參數數據包較大,提供者比消費者個數多,提供者壓力較大,可傳文件

適用場景:頁面傳輸,文件傳輸,或與原生hessian服務互操作

約束

參數及返回值需實現Serializable接口

參數及返回值不能自定義實現List, Map, Number, Date, Calendar等接口,只能用JDK自帶的實現,因為hessian會做特殊處理,自定義實現類中的屬性值都會丟失

exchange( 信息交換層)

封裝請求響應模式,同步轉異步

以Request, Response為中心,擴展接口為Exchanger,?ExchangeChannel,?ExchangeClient,?ExchangeServer

transport( 網絡傳輸層)

抽象mina和netty為統一接口

以Message為中心,擴展接口為Channel,?Transporter,?Client,?Server,?Codec

選擇

Netty

性能較好(推薦使用)

一次請求派發兩種事件,需屏蔽無用事件

Mina

老牌NIO框架,穩定

待發送消息隊列派發不及時,大壓力下,會出現FullGC

Grizzly

Sun的NIO框架,應用於GlassFish服務器中

線程池不可擴展,Filter不能攔截下一Filter

serialize( 數據序列化層)

可復用的一些工具

擴展接口為Serialization,?ObjectInput,?ObjectOutput,?ThreadPool

選擇

Hessian

性能較好,多語言支持(推薦使用)

Hessian的各版本兼容性不好,可能和應用使用的Hessian沖突,Dubbo內嵌了hessian3.2.1的源碼

Dubbo

通過不傳送POJO的類元信息,在大量POJO傳輸時,性能較好

當參數對象增加字段時,需外部文件聲明

Json

純文本,可跨語言解析,缺省采用FastJson解析

性能較差

Java

Java原生支持

性能較差

關系說明

在RPC中,Protocol是核心層,也就是只要有Protocol + Invoker + Exporter就可以完成非透明的RPC調用,然後在Invoker的主過程上Filter攔截點。

圖中的Consumer和Provider是抽象概念,只是想讓看圖者更直觀的了解哪些類分屬於客戶端與服務器端,不用Client和Server的原因是Dubbo在很多場景下都使用Provider, Consumer, Registry, Monitor劃分邏輯拓普節點,保持統一概念。

而Cluster是外圍概念,所以Cluster的目的是將多個Invoker偽裝成一個Invoker,這樣其它人只要關註Protocol層Invoker即可,加上Cluster或者去掉Cluster對其它層都不會造成影響,因為只有一個提供者時,是不需要Cluster的。

Proxy層封裝了所有接口的透明化代理,而在其它層都以Invoker為中心,只有到了暴露給用戶使用時,才用Proxy將Invoker轉成接口,或將接口實現轉成Invoker,也就是去掉Proxy層RPC是可以Run的,只是不那麽透明,不那麽看起來像調本地服務一樣調遠程服務。

而Remoting實現是Dubbo協議的實現,如果你選擇RMI協議,整個Remoting都不會用上,Remoting內部再劃為Transport傳輸層和Exchange信息交換層,Transport層只負責單向消息傳輸,是對Mina,Netty,Grizzly的抽象,它也可以擴展UDP傳輸,而Exchange層是在傳輸層之上封裝了Request-Response語義。

Registry和Monitor實際上不算一層,而是一個獨立的節點,只是為了全局概覽,用層的方式畫在一起

Dubbo模塊分包?模塊

dubbo-common 公共邏輯模塊,包括Util類和通用模型。

dubbo-remoting 遠程通訊模塊,相當於Dubbo協議的實現,如果RPC用RMI協議則不需要使用此包。

dubbo-rpc 遠程調用模塊,抽象各種協議,以及動態代理,只包含一對一的調用,不關心集群的管理。

dubbo-cluster 集群模塊,將多個服務提供方偽裝為一個提供方,包括:負載均衡,?容錯,路由等,集群的地址列表可以是靜態配置的,也可以是由註冊中心下發。

dubbo-registry 註冊中心模塊,基於註冊中心下發地址的集群方式,以及對各種註冊中心的抽象。

dubbo-monitor 監控模塊,統計服務調用次數,調用時間的,調用鏈跟蹤的服務。

dubbo-config 配置模塊,是Dubbo對外的API,用戶通過Config使用Dubbo,隱藏Dubbo所有細節。

dubbo-container 容器模塊,是一個Standlone的容器,以簡單的Main加載Spring啟動,因為服務通常不需要Tomcat/JBoss等Web容器的特性,沒必要用Web容器去加載服務

與分層的不同點在於

container為服務容器,用於部署運行服務,沒有在層中畫出。

protocol層和proxy層都放在rpc模塊中,這兩層是rpc的核心,在不需要集群時(只有一個提供者),可以只使用這兩層完成rpc調用。

transport層和exchange層都放在remoting模塊中,為rpc調用的通訊基礎。

serialize層放在common模塊中,以便更大程度復用

模型?Protocol是服務域,它是Invoker暴露和引用的主功能入口,它負責Invoker的生命周期管理Invoker是實體域,它是Dubbo的核心模型,其它模型都向它靠擾,或轉換成它,它代表一個可執行體,可向它發起invoke調用,它有可能是一個本地的實現,也可能是一個遠程的實現,也可能一個集群實現Invocation是會話域,它持有調用過程中的變量,比如方法名,參數等基本原則?采用Microkernel + Plugin模式,Microkernel只負責組將Plugin,Dubbo自身的功能也是通過擴展點實現的,也就是Dubbo的所有功能點都可被用戶自定義擴展所替換采用URL作為配置信息的統一格式,所有擴展點都通過傳遞URL攜帶配置信息擴展點加載?Dubbo的擴展點加載從JDK標準的SPI(Service Provider Interface)擴展點發現機制加強而來在擴展類的jar包內,放置擴展點配置文件:META-INF/dubbo/接口全限定名,內容為:配置名=擴展實現類全限定名,多個實現類用換行符分隔註意:這裏的配置文件是放在你自己的jar包內,不是dubbo本身的jar包內,Dubbo會全ClassPath掃描所有jar包內同名的這個文件,然後進行合並Provider暴露服務的過程?具體服務到Invoker的轉換

ServiceConfig:ref對外提供服務實際類

ProxyFactory:getInvoker()

JavassistProxyFactory

JdkProxyFactory

Invoker轉換為Exporter

Invoker:AbstractProxyInvoker的實例

Protocal:export()

DubboProtocol

Dubbo協議的Invoker轉為Exporter發生在DubboProtocol類的export方法,它主要是打開socket偵聽服務,並接收客戶端發來的各種請求,通訊細節由Dubbo自己實現

HessianProtocol

InjvmProtocol

它通過Spring或Dubbo或JDK來實現RMI服務,通訊細節這一塊由JDK底層來實現,這就省了不少工作量

RmiProtocol

WebServiceProtocol

Export

Consumer消費服務的過程?把遠端服務轉為Invoker

ReferenceConfig

Protocol:refer()

DubboProtocol

HessianProtocol

InjvmProtocol

RmiProtocol

WebServiceProtocol

把Invoker轉為客戶端需要的接口

Invoker

DubboInvoker

HessianInvoker

InjvmInvoker

RmiInvoker

WebServiceInvoker

ProxyFactory:getProxy()

JavassistProxyFactory

JdkProxyFactory

ref

過程:首先ReferenceConfig類的init方法調用Protocol的refer方法生成Invoker實例(如上圖中的紅色部分),這是服務消費的關鍵。接下來把Invoker轉換為客戶端需要的接口

無處不在的Invoker?由於Invoker是Dubbo領域模型中非常重要的一個概念,很多設計思路都是向它靠攏服務消費者Invoker

用戶代碼通過這個proxy調用其對應的Invoker(DubboInvoker、 HessianRpcInvoker、 InjvmInvoker、 RmiInvoker、 WebServiceInvoker中的任何一個),而該Invoker實現了真正的遠程服務調用

服務提供者Invoker

被封裝成為一個AbstractProxyInvoker實例,並新生成一個Exporter實例。這樣當網絡通訊層收到一個請求後,會找到對應的Exporter實例,並調用它所對應的AbstractProxyInvoker實例,從而真正調用了服務提供者的代碼

線程模型?過程

Proxy

Client

Transporter

Header ->?Codec

Body ->?Serialization

Server

Dispatcher

ThreadPool

Implementation

Dispatcher

all?所有消息都派發到線程池,包括請求,響應,連接事件,斷開事件,心跳等

direct?所有消息都不派發到線程池,全部在IO線程上直接執行

message?只有請求響應消息派發到線程池,其它連接斷開事件,心跳等消息,直接在IO線程上執行

execution?只請求消息派發到線程池,不含響應,響應和其它連接斷開事件,心跳等消息,直接在IO線程上執行

connection?在IO線程上,將連接斷開事件放入隊列,有序逐個執行,其它消息派發到線程池

ThreadPool

fixed?固定大小線程池,啟動時建立線程,不關閉,一直持有。(缺省)

cached?緩存線程池,空閑一分鐘自動刪除,需要時重建

limited?可伸縮線程池,但池中的線程數只會增長不會收縮。(為避免收縮時突然來了大流量引起的性能問題)

4,增強功能

並發控制

連接控制: 連接數控制

分組聚合: 分組聚合返回值,用於菜單聚合等服務

泛化引用: 泛化調用,無需業務接口類進行遠程調用,用於測試平臺,開放網關橋接等

異步調用

延遲暴露: 延遲暴露服務,用於等待應用加載warmup數據,或等待spring加載完成

延遲連接: 延遲建立連接,調用時建立

隱私傳參: 附加參數

5,Dubbo擴展

方法

OSGI

Equinox

Eclipse, HSF

META-INF/MANIFEST.MF

IoC

Spring

META-INF/spring/beans.xml

SPI

java.util.ServiceProvider

JDBC, MessageDigest, ScriptEngine

META-INF/services/com.xx.Xxx

Dubbo SPI?Microkernel & SPIProtocol & ProxyFactory & FilterCluster & Directory & Router & LoadBalanceTransporter & Serialization & ThreadPoolTelnetHandler & StatusChecker

6,Dubbo設計原則

模塊分包原則

復用度

包中的類應該有同樣的重用可能性

緊密協作的類應該放在一個包

對於變化因子,包中的類應全改或全不改

變化應在包內終止,而不傳播到其它包

發布的粒度和復用度相同

穩定度

被依賴的包應該總是比依賴者更穩定

不要讓一個穩定的包依賴於不穩定包

單向依賴,無環依賴

抽象度

越穩定的包應該越抽象

穩定的包不抽象將導致擴展性極差

抽象的包不穩定將導致其依賴包跟隨變化

框架擴展原則?微核 +插件體系

OSGI

IoC

SPI

平等對待第三方?

Dogfooding

框架自己的功能也要擴展點實現

甚至微核的加載方式也可以擴展

Autowire

裝配邏輯由擴展點之間互助完成

杜絕硬編碼的橋接和中間代碼

Cascading

層疊擴展粒度,逐級細分

由大的擴展點加載小的擴展點

Law of Demeter

只與×××的擴展點交互,間接轉發

保持行為單一,輸入輸出明確

外置生命周期

API傳入參數,SPI擴展點實例

盡量引用外部對象的實例,而不類元

正確:userInstance.xxx()

錯誤:Class.forName(userClass).newInstance().xxx()

盡量使用IoC註入,減少靜態工廠方法調用

正確:setXxx(xxx)

錯誤:XxxFactory.getXxx();?applicationContext.getBean(“xxx”)

最少化概念模型一致性數據模型

Dubbo統一URL模型

所有配置信息都轉換成URL的參數

所有的元信息傳輸都采用URL

所有接口都可以獲取到URL

領域劃分原則?服務域

指產品主要功能入口,同時負責實體域和會話域的生命周期管理。

Velocity的Engine

Spring的BeanFactory

實體域

表示你要操作的對象模型,不管什麽產品,總有一個核心概念,大家都繞圍它轉。

Velocity的Template

Spring的Bean

會話域

表示每次操作瞬時狀態,操作前創建,操作後銷毀。

Velocity的Context

Spring的Invocation

領域模型劃分優勢

結構清晰,可直接套用

充血模型,實體域帶行為

可變與不可變狀態分離,可變狀態集中

所有領域線程安全,不需要加鎖

領域模型線程安全性

服務域

通常服務域是無狀態,或者只有啟動時初始化不變狀態,所以天生線程安全,只需單一實例運行

實體域

通常設計為不變類,所有屬性只讀,或整個類引用替換,所以是線程安全的

會話域

保持所有可變狀態,且會話域只在線程棧內使用,即每次調用都在線程棧內創建實例,調用完即銷毀,沒有競爭,所以線程安全

接口分離原則?API & SPI

聲明式API(Dubbo API):描述需要什麽

ServiceConfig

ReferenceConfig

RpcContext

過程式SPI(Dubbo SPI):描述怎麽實現

Protocol

Transporter

LoadBalance

API可配置,一定可編程

配置用於簡化常規使用

編程接口用於框架集成

API區分命令與查詢

命令:無返回值表示命令,有副作用

查詢:有返回值表示查詢,保持冪等,無副作用

組件協作原則?管道 v.s. 派發

管道

組合行為

主功能以截面實現

比如:Servlet

派發

策略行為

主功能以事件實現

比如:?Swing

分布 v.s. 共享

分布

在行為交互為主的系統是適用

狀態通過行為傳遞

共享

在以管理狀態為主的系統中適用

狀態通過倉庫共享

主過程攔截

Web框架的請求響應流

ORM框架的SQL執行

Service框架的調用過程

反例:IBatis2在SQL執行過程中沒有設攔截點,導致添加安全或日誌攔截,執行前修改分頁SQL等,不得不hack源代碼

事件派發

過程

執行前後

觸發附帶非關鍵行為

狀態

值的變化

觸發狀態觀察者行為

關鍵路徑

關鍵路徑

采用攔截鏈分離職責

保持截面功能單一,不易出問題

非關鍵路徑

采用後置事件派發

確保派發失敗,不影響主過程運行

協作防禦

可靠性分離

可靠操作

不可靠操作 (盡量縮小)

狀態分離

無狀態

有狀態 (盡量縮小)

不可變類 (盡量final)

狀態驗證

盡早失敗

前置斷言 + 後置斷言 + 不變式

異常防禦,但不忽略異常

異常信息給出解決方案

日誌信息包含環境信息

降低修改時的誤解性,不埋雷

避免基於異常類型的分支流程

保持null和empty語義一致

功能演進原則?開閉原則

對擴展開放

對修改關閉

軟件質量的下降,來源於修改

替換整個實現類,而不是修改其中的某行

增量式 v.s.擴充式

Dubbo增量式擴展

Remoting

Transport:

單向消息發送,抽象Mina/Netty

Exchange:

封裝Request-Respose語義

調用兩次單向消息發送完成

RPC

Portocol:

協議實現,不透明,點對點

Cluster:

將集群中多個提供者偽裝成一個

Proxy:

透明化接口,橋接動態代理

在高階附加功能

盡可能少的依賴低階契約,用最少的抽象概念實現功能

當低階切換實現時,高階功能可以繼續復用

7,Dubbo編碼約定

異常和日誌:

盡可能攜帶完整的上下文信息,比如出錯原因,出錯的機器地址,調用對方的地址,連的註冊中心地址,使用Dubbo的版本等。

盡量將直接原因寫在最前面,所有上下文信息,在原因後用鍵值對顯示。

拋出異常的地方不用打印日誌,由最終處理異常者決定打印日誌的級別,吃掉異常必需打印日誌。

打印ERROR日誌表示需要報警,打印WARN日誌表示可以自動恢復,打印INFO表示正常信息或完全不影響運行。

建議應用方在監控中心配置ERROR日誌實時報警,WARN日誌每周匯總發送通知。

RpcException是Dubbo對外的唯一異常類型,所有內部異常,如果要拋出給用戶,必須轉為RpcException。

RpcException不能有子類型,所有類型信息用ErrorCode標識,以便保持兼容。

配置和URL:?配置對象屬性首字母小寫,多個單詞用駝峰命名(Java約定)。配置屬性全部用小寫,多個單詞用"-"號分隔(Spring約定)。URL參數全部用小寫,多個單詞用"."號分隔(Dubbo約定)。盡可能用URL傳參,不要自定義Map或其它上下文格式,配置信息也轉成URL格式使用。盡量減少URL嵌套,保持URL的簡潔性。單元和集成測試:?單元測試統一用JUnit和EasyMock,集成測試用TestNG,數據庫測試用DBUnit。保持單元測試用例的運行速度,不要將性能和大的集成用例放在單元測試中。保持單元測試的每個用例都用try...finally或tearDown釋放資源。減少while循環等待結果的測試用例,對定時器和網絡的測試,用以將定時器中的邏輯抽為方法測試。對於容錯行為的測試,比如failsafe的測試,統一用LogUtil斷言日誌輸出。擴展點基類與AOP:?AOP類都命名為XxxWrapper,基類都命名為AbstractXxx。擴展點之間的組合將關系由AOP完成,ExtensionLoader只負載加載擴展點,包括AOP擴展。盡量采用IoC註入擴展點之間的依賴,不要直接依賴ExtensionLoader的工廠方法。盡量采用AOP實現擴展點的通用行為,而不要用基類,比如負載均衡之前的isAvailable檢查,它是獨立於負載均衡之外的,不需要檢查的是URL參數關閉。對多種相似類型的抽象,用基類實現,比如RMI,Hessian等第三方協議都已生成了接口代理,只需將將接口代理轉成Invoker即可完成橋接,它們可以用公共基類實現此邏輯。基類也是SPI的一部分,每個擴展點都應該有方便使用的基類支持。模塊與分包:?基於復用度分包,總是一起使用的放在同一包下,將接口和基類分成獨立模塊,大的實現也使用獨立模塊。所有接口都放在模塊的根包下,基類放在support子包下,不同實現用放在以擴展點名字命名的子包下。盡量保持子包依賴父包,而不要反向。

作者:歐陽海陽
鏈接:https://www.jianshu.com/p/1d17c639a4d6
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權並註明出處。

RPC框架Dubbo深入分析