松花皮蛋的黑板報
  • 分享在京東工作的技術感悟,還有JAVA技術和業內最佳實踐,大部分都是務實的、能看懂的、可復現的

掃一掃
關注公眾號

微服務架構之我們應該從Dubbo中學到什么

博客首頁文章列表 松花皮蛋me 2019-05-15 22:24

一、 模塊分包

整體上按分層進行分包,然后每個包又分API包和具體的方案包,從中提煉出三個需要注意的點

  • 1.1復用度
    1) 包中的類應具有相同的重用可能性
    2) 緊密協作的類應放在同一包
    3) 對于變化因子,包中的類應全改或全不改
    4) 變化應在包內終止,不應傳播到其他包
  • 1.2 穩定度
    1) 被依賴的包總是比依賴者更穩定
    2) 不要讓一個穩定的包依賴于不穩定的包
    3) 單向依賴,無環依賴
  • 1.3 抽象度
    1) 越穩定的包應越抽象
    2) 抽象的包不穩定導致其所有依賴包處于經常的變化中

二、 框架擴展之微核和插件

大凡發展的比較好的框架,都遵守微核的理念, Eclipse的微核是OSGi(依賴META-INF/MANIFEST.MF配置), Spring的微核是BeanFactory,Maven的微核是Plexus,Dubbo的微核是SPI(依賴META-INF/services/com.xxx.xxx配置)。通常核心是不應該帶有功能性的,而是一個生命周期和集成容器,負責加載、卸載、運行插件模塊,這樣各功能可以通過相同的方式交互及擴展,并且任何功能都可以被替換。如果做不到微核,至少要平等對待第三方,即原作者能實現的功能,擴展者應該可以通過擴展的方式全部做到,原作者要把自己也當作擴展者,這樣才能保證框架的可持續性及由內向外的穩定性。

三、 框架擴展之平等對待第三方

  • 3.1 Dogfoodin-吃自己的狗糧
    1) 框架自己的功能具備擴展點實現
    2) 微核的加載方式也可以擴展
  • 3.2 Autowire-依賴注入
    1) 裝配邏輯由擴展點之間互助完成
    2) 杜絕硬編碼的橋接和中間代碼
  • 3.3 Cascading-層疊
    1) 層疊擴展粒度,逐級細分
    2) 由大的擴展點加載小的擴展點
  • 3.4 Low of Demeter-最少知識原則
    1) 只與觸手可及的擴展點交互,間接轉發
    2) 保持行為單一,輸入輸出明確
    3) 一個對象應該對其他對象有最少的了解

四、 框架擴展之Filter-Chain模型

將一個事件處理流程分派到一組執行對象上,這一組執行對象形成一個鏈式結構,事件處理在這一組對象上進行傳遞

五、 框架擴展之外置生命周期

框架不應該控制實現類的生命周期,框架最多提供工具類輔助管理,而不是絕對控制,應滿足下面的規范

  • 1. 盡量引用外部對象的實例,不是類元。正確示例:usrInstance.xxx(),Spring IoC,錯誤示例:Class.forName(userClass).newInstance().xxx
  • 2. 使用IoC注入,減少靜態工廠方法。正確示例:setXxx(xxx),錯誤示例:XxxFactory.getXxx(),applicationContext.getBean(“xxx”)

六、 框架擴展之一致性數據模型

URL作為Dubbo一個公共契約,所有的擴展點都包含URL參數,URL作為上下文信息貫穿整個擴展點設計體系。所有的配置信息都轉換成URL的參數,所有的元信息傳輸都采用URL,所有的接口都可以獲取到URL

七、 領域模型劃分

  • 1. 服務域:也稱為行為域,作為組件的功能集,同時負責實體域和會話域的生命周期管理,如Velocity的Engine\Spring的BeanFactory
  • 2. 實體域:表示操作的對象模型,任何產品都有核心概念,圍繞它轉,如Velocity的Templcat\Spring的Bean
  • 3. 會話域: 表示每次操作或運行的瞬時狀態,操作前創建,操作后銷毀,如Spring中的Invocation

領域模型劃分好處:結構清晰,可直接套用;充血模型,實體域帶行為;可變和不可變狀態分離,可變狀態集中;所有領域線程安全,不需要加鎖

八、 Dubbo核心領域模型

  • 1. 服務域Protocol: Invoker暴露和引用的主功能入口,負責Invoker的生命周期管理
  • 2. 實例域Invoker: 它是Dubbo的核心模型,任何模型都向它靠攏或者轉換成它,它代表一個可執行體,可它向發起Invoke調用,可能是一個本地實現,也可能是遠程調用,也有可能是集群實現
  • 3. 會話域Invocation: 持有調用過程的變量,比如方法名和參數等

九、 領域模型線程安全性

  • 1. 服務域:通常無狀態,是線程安全的
  • 2. 實體域:通過設計為不變類,所有屬性只讀,或整個類引用替換,是線程安全的
  • 3. 會話域:保持所有可變狀態,且會話域只在線程棧內使用,每次調用都在線程棧內創建實例,調用完即銷毀,是線程安全的

十、 API和SPI分離

Dubbo中的API如ServiceConfig\ReferenceConfig\RpcContext是給使用者使用的,Dubbo中的SPI如Protocol\Transporter\LoadBalance,是給擴展者使用的,API應該是聲明式的,描述需要什么,SPI應該是過程式的,描述怎么實現。它們不應該混在一起,使用者不應該看到擴展者寫的實現

十一、 API可配置一定可編程

  • 1. 配置用于簡化常規使用
  • 2. 編程接口用于框架集成

十二、管道和派發

管道一般適用于組合行為,主功能以截面AOP實現,比如Servlet。派發一般適用于策略行為,主功能以事件Event實現,比如Flux

十三、主過程攔截

沒有哪個公用的框架可以Cover住所有的需求,不管是Web框架的請求響應流、ORM框架的SQL-Mapping過程,還是Service框架的調用過程,允許外置行為是框架的基本擴展方式,不然如果需要添加安全、日記或者修改分頁SQL等不得不hack源代碼了

十四、Dubbo調用過程攔截

Dubbo中使用全管道設計,框架自身邏輯,均使用截面攔截實現,比如常見于消費端的context\collect\generic\activelimit\monitor\future等鏈式過濾器,常見于生產端的token\exception\echo\accesslog\trace\executelimit等鏈式過濾器

十五、事件派發

攔截器是在切點執行前后生效的,它是干預過程的,會觸發非關鍵行為,而事件是基于狀態數據的,會觸發狀態觀察者行為

十六、Reactor和Proactor事件驅動模型

Reactor模型關注就緒狀態,比如可讀了就通知我們主動去讀,類似Linux epoll,而proactor關心的是完成狀態,比如我們指定存放數據的內存地址(Buffer)和讀事件,當它讀完了就會通知我們,類似Windows IO completion port

十七、 暴露/引用/調用事件

Dubbo在關鍵路徑上采用攔截鏈分離職責,保持界面功能單一,不易出問題。在非關鍵路徑上,采用后置派發,即使派發失敗也不會影響主流程運行

十八、協作防御

  • 1. 可靠性分離。不可靠操作盡量縮小
  • 2. 狀態分離。有狀態盡量縮小可變域,不可變類盡量聲明為final
  • 3. 狀態驗證。盡早失敗,在有傳入參數或者狀態變化時,均在入口處全部斷言
  • 4. 異常防御。不要生吃異常,應該盡量保證異常信息給出解決方案,日記信息包含上下文
  • 5. 降低修改時的無界性,不埋雷。避免基于異常類型的分支流程,同時保持NULL和Empty語義一致

十九、開閉原則

開閉原則,對擴展開放,對修改關閉,因為風險往往來自于修改。擁抱變化時應該繼承原有類然后重寫方法擴展邏輯,而不是修改原來的類

二十、增量式和擴充式

如果現有一個無狀態消息發送的場景,后來新增一個會話消息發送需求,如果采用增量式擴展,無狀態消息發送原封不動,同步消息發送,在無狀態消息基礎上加一個 Request/Response 處理,會話消息發送,再加一個 SessionRequest/SessionResponse 處理

二十一、Dubbo增量式擴展

傳統的C\S模型是一問一答模式,而Dubbo的RPC模型中包括了Proxy\Custer\Protocol, Protocol只負責協議實現,它是不透明的、點對點的,Cluster只負責將集群中多個提供者偽裝成一個,Proxy只負責透明化接口,橋接動態代理,整體的架構非常容易擴展

二十二、在高階附加功能

盡可能少的依賴低階契約,用最少的抽象概念實現功能。當低階切換實現時,高階功能可以繼續復用

二十三、Dubbo高階泛化調用

以PHP到Router的request body中的方法名和方法參數作為Router遠程調用后端Java服務的入參,最后將遠程調用的result返回給PHP端就是兼容Restful服務的高階泛化調用

二十四、總結

黑龙江6+1开奖结果查询