dubbo 使用者手冊初讀
參考:dubbo 使用者手冊官方位址
需求
- 服務注冊管理,提供負載均衡和故障轉移;
- 自動治理服務間的依賴關系;
- 動态擴容;
架構
- 注冊中心和消費者、服務提供者之間是長連接配接,消費者和提供者與監控中心間使用短連接配接;
- 每分鐘同步一次監控資料到監控中心;
快速啟動
- 疑問:一個服務可以是提供者也可以是消費者,如果沒有提供 dubbo:protocol 配置,即沒有使用dubbo協定暴露端口,那肯定不是提供者?
- 疑問:使用multicast廣播注冊中心作為服務發現位址,寫死的配置如何避免注冊中心單點故障,如果需要調整注冊中心位址怎麼辦?
- multicast廣播注冊中心适用于開發測試環境,優點是無需安裝注冊中心服務,缺點是依賴于網絡路由,跨機房有風險;
依賴
- 疑問:依賴于spring,如果應用中spring使用其他版本會不會有沖突?
- 疑問:理論上說可以不依賴任何第三方包,要如何配置,有何缺點?
政策選擇成熟度
- 注冊中心
- zookeeper,推薦,使用廣,依賴于zk的穩定性
- redis,支援基于用戶端雙寫的方式,性能高
- multicast,無需安裝,去中心化,依賴于網絡拓撲,适用于開發測試環境
- simple,沒有叢集支援,可能單點故障,試用
- 遠端調用協定
- dubbo協定,推薦,NIO複用長連接配接,連接配接池并發處理,減少握手次數,加大并發效率較高,缺點是單個連接配接傳輸大檔案時會成為瓶頸;
- Rmi協定,基于TCP,可與原生RMI互操作,偶爾會連接配接失敗,需要重建stub;
- Hession協定,基于http,可以原生hession互操作,需要hession.jar支援,http短連接配接消耗大;
- NIO架構
- netty,推薦,性能好
- Mina,老牌架構,穩定
- 序列化
- Hession序列化,推薦,性能較好,多語言支援,缺點是Hession各版本間可能存在相容問題,dubbo使用hessian3.2.1;
- dubbo序列化,不傳送pojo資訊,在大量pojo需要傳輸時性能較好,缺點是當參數對象增加字段時,需要外部檔案聲明;
- json序列化,純文字,跨語言,預設采用FastJson解析,缺點是性能相對較差;
- java序列化,原生支援,缺點是性能較差;
- java代理
- Javassist代理,推薦,通過位元組碼生成代理類,代替反射,性能好,缺點是依賴于javassist.jar,perm記憶體配置大一些,如128M;
- java代理,jdk原生支援,性能較差;
- 叢集模式
- Failover 叢集,失敗自動切換,讀操作時推薦,缺點是重試會增加延遲;
- Failfast 叢集,快速失敗,隻發起一次,失敗立即報錯,适合非幂等的寫操作,缺點是如果有機器正在重新開機,可能會發生調用失敗;
- Failsafe 叢集,失敗安全,出現失敗時直接忽略,适用于收集日志等,缺點是會丢消息;
- Failback 叢集,失敗自動恢複,背景記錄失敗請求定時重發,适用于消息通知等,缺點是不可靠,重新開機丢失;
- Forking 叢集,并行調用多個伺服器,一個成功即可,适用于實時要求高的請求,缺點是會浪費資源;
- Broadcast 叢集,廣播調用所有提供者,有一個失敗即報錯,适用于更新本地服務狀态,如本地日志或緩存等,速度慢;
- 負載均衡政策
- 随機,推薦,按照權重設定随機比例,缺點是重試時可能造成瞬間的壓力不均;
- 輪詢,按照公約後權重設定輪詢比例,缺點是存在慢的機器積累請求的問題,極端情況下會出現雪崩現象;
- 最少活躍調用數,使慢的機器收到更少請求,缺點是不支援權重;
- 一緻性hash,相同參數總是發到同一個提供者,如果提供者挂了,通過虛拟節點分攤到其他提供者,缺點是壓力不均;
- 路由規則
- 條件路由,推薦,基于條件判斷的路由,簡單易用,缺點是複雜場景下很難使用條件描述;
- 腳本路由,基于腳本,功能強大,缺點是有可能成為後門;
- java容器
- spring容器,推薦,自動加載META-INF/spring下的配置檔案;
- Jetty容器,啟動内嵌的Jetty,缺點是大量通路頁面時,會影響伺服器的線程和記憶體;
- Log4j容器,自動配置log4j,自動按照程序劃分目錄,缺點是使用者不能控制log4j的配置,不夠靈活;
配置
- dubbo:provider,dubbo:protocol 和 dubbo:service中某屬性沒有配置時,使用此預設配置;
- dubbo:consumer,dubbo:reference中某屬性沒有配置時,使用此預設配置;
- dubbo:reference,引用配置預設是延遲加載,即隻有被注入到其他bean或者getbean時才會初始化,可以加上init=”true”表示立即加載;
- 服務屬性優先級,方法優于接口和全局,消費優于提供:消費方方法級>提供方方法級>消費方接口級>提供方方法級>消費方全局配置>提供方全局配置
- 建議由服務提供方設定逾時屬性,因為服務提供方最清楚可能的消耗時間;
dubbo 使用者手冊初讀
叢集容錯
- 如何選擇到最終的提供方?
線程模型
- server收到請求後,是直接在IO線程中處理響應(A),還是送出到線程池中(B)?
- 如果隻是簡單處理那A響應更快,因為省去了送出到B後的線程排程,但是如果這個請求要發起别的IO請求或者處理邏輯較慢,會導緻接收不到其他請求;
- Dispatcher 政策,all-所有消息都派發到線程池,direct-所有消息都在IO線程上直接執行,message-請求響應派發到線程池,連接配接斷開時間、心跳檢測等直接在IO線程執行,..
- ThreadPool 政策,fixed-預設政策,固定線程池,不關閉一直持有,cached-緩存線程池,空閑一分後清理,limited-隻增不減,防止大流量突襲造成性能問題;
回聲測試
- 用于檢測服務是否可用,可用于監控等場景;
- 所有暴露的服務均實作EchoService接口,直接強轉後調用$echo方法判斷即可;
本地存根
- 适用于本地調用前後做ThreadLocal緩存、參數驗證、失敗後僞造容錯資料等;
- 講proxy執行個體傳遞給Stub(Stub需包含可傳入Proxy的構造方法);
-
<dubbo:service inter stub="com.foo.BarServiceStub" />
本地僞裝
- Mock,用于服務降級,是Stub的子集,隻有在調用過程中出現RpcException時才執行,此時可傳回失敗提示資訊而不是抛錯,包含無參的構造方法;
-
<dubbo:service inter mock="com.foo.BarServiceMock" />
并發控制
- 用戶端或者服務端控制并發數量,executes-總的調用并發不能超過指定數量,actives-每個用戶端的調用并發不能超過指定數量;
連接配接控制
- 服務端連接配接控制,acceptes-接受的連接配接數不超過指定數量;
- 用戶端連接配接控制,conntections-使用的連接配接數不超過指定數量;
粘滞連接配接
- 讓調用者盡可能請求同一個提供者,除非該提供者挂了才換另一台;
-
預設開啟延遲連接配接以減少長連接配接數;<dubbo:protocol name="dubbo" sticky="true" />
- 疑問:隻在dubbo協定下才有效?
令牌驗證
- 防止消費者繞過注冊中心直接調用提供者;
- 注冊中心統一管理,靈活管理授權方式,不需要修改提供者;
- 有随機token(UUID)和固定token(指定的)兩種;
dubbo 使用者手冊初讀
路由規則
- 支援條件路由和腳本路由,腳本路由更靈活,但是有後門的風險;
- 可以實作黑白名單、讀寫分離(按照方法名路由)、重要應用隔離、分網段隔離等;
配置規則
- 可以實作禁用提供者、調整權重、調整負載均衡政策、服務降級(臨時屏蔽出錯的非關鍵服務)等;
服務降級
- 配置規則中的一種,用于降低服務不可用時對調用方的影響;
-
在消費者方直接傳回null,不發起調用;mock=force:return+null
-
失敗後傳回null;mock=fail:return+null
優雅停機
- 停機時,提供方不在接受新請求,等待已有請求運作完畢或逾時,調用方不在發起新請求,或等待已有響應運作完畢或逾時;
- 原理是使用JDK的ShutdownHook,是以強制關閉(kill -9 PID)是不會觸發的;
注冊中心
ZooKeeper 注冊中心
- zk是一個樹形的目錄服務,支援消息訂閱、推送,異常恢複等;
- provider啟動時将URL寫入到 /dubbo/ServiceA/providers下;
- consumer啟動時訂閱/dubbo/ServiceA/providers下的提供者位址,同時将URL寫入到/dubbo/ServiceA/consumers下;
- 監控中心啟動時,訂閱/dubbo/ServiceA下的所有提供者和消費者URL位址;
- 提供者斷電時,監控中心能自動删除提供者資訊;
- 注冊中心重新開機時,自動恢複注冊資料及訂閱請求;
- 支援訂閱或注冊失敗時背景儲存,定時重新開機;
線上運維
- 同時支援telnet指令和http,支援檢視服務提供者和消費者狀态、服務上下線等;
最佳實踐
分包
- 接口、pojo、異常都放在api包中;
粒度
- 服務接口盡可能大粒度,表示某一個功能而不是某一個功能的一個步驟,減少分布式事務的出現;
- 使用表義的接口命名;
版本
- 每個服務都要定義版本,建議兩段版本号,第三段版本号隻做相容更新時使用;
- 更新時先升一半提供者,再升所有消費者,最後升剩下的一半提供者;
- 疑問:為什麼?如果更新後的提供者有問題需要復原,可以直接将消費者切換到使用舊版的提供者?
相容性
- 增加接口或pojo屬性,可向後相容,如果删除或更新,或者enum新增字段也不相容,需變更版本号;
枚舉值
- 不建議使用肯定會擴充的字段作為enum,此時建議使用String;
- 新增enum類型,如果是接口傳回新增類型,先更新消費者,如果是接口入參新增類型,先更新提供者;
序列化
- 所有服務參數和傳回值都是值傳遞,不支援引用傳遞;
異常
- 建議服務間使用異常而不是錯誤碼;
- 封裝如dao、sql異常等,防止消費者序列化錯誤;
- 如果出于性能優化考慮,重寫異常的fillInStack方法,不填充後續異常堆棧;
- 不建議抛出checked異常,消費者通常不知道如何處理;
調用
-
應加在合适的復原邊界上,而不是隻因為是dubbo服務就加;try...catch
- 提供者方要進行參數校驗,如果要提高性能,可以在api包中添加Stub完成校驗;
推薦用法
- 在Provider上多配置Consumer端屬性,因為提供者對于服務性能參數配置更了解,如 timeout、loadbalance(預設random)、retry(預設2,即一共1+2=最多3次調用)、actives(消費者超過最大并發限制時新的調用請求進入WAIT,直到timeout);
- 在Provider上多配置Provider端屬性,如executes、threads(線程池大小);
- 配置負責人(應用、服務、引用等),ower屬性;
- 配置dubbo緩存檔案,
,用于注冊中心挂掉時從緩存檔案加載提供者清單,保證可用性,注意多程序使用不同的目錄檔案;<dubbo:registry file="${user.home}/output/dubbo.cache"/>
- 監控配置,服務推薦使用固定端口,這樣在注冊中心挂掉後可以通過緩存檔案保證可用性;
- 推薦使用xml配置而不是properties;