Node 呼叫 dubbo 服務的探索及實踐

JAVA架構發表於2019-03-24

1. 背景介紹

我們公司是杭州的一家電商公司,公司內的技術體系較多,主要語言有了JAVA/PHP/Node,其中在19年的時候,公司制定了去PHP化的計劃,將後端邏輯沉澱到Java服務化當中,而部分服務化呼叫相關業務則需要Node扛起,而與Java進行通訊則需要經過Dubbo,由此我們以Consumer的角色來探索與研究如何用Node呼叫Dubbo.

呼叫鏈路


2.Dubbo簡介

2.1 什麼是dubbo

Dubbo是一款高效能、輕量級的開源Java RPC框架,它提供了三大核心能力:面向介面的遠端方法呼叫,智慧容錯和負載均衡,以及服務自動註冊和發現。

2.2 流程圖

呼叫流程圖

  • Provider : 暴露服務的服務提供方。
  • Consumer : 呼叫遠端服務的服務消費方。
  • Registry: 服務註冊與發現的註冊中心。
  • Monitor: 統計服務的呼叫次調和呼叫時間的監控中心。
  • Container: 服務執行容器。

3. 具體實現

3.1 協議選擇

官方雖然支援了很多種協議,但是真正適合於我們的協議並不是很多,譬如rmi協議主要是面向Java工程之間的呼叫,並不適合用於異構語言RPC場景,所以我們接下來只針對部分協議進行分析,並結合我們的實際的業務場景,最後來篩選出適合我們的協議.

連線個數 連線方式 傳輸協議 傳輸方式 序列化 適用範圍 適用場景
dubbo 單連線 長連線 TCP NIO 非同步傳輸 Hessian 二進位制序列化 傳入傳出引數資料包較小,消費者比提供者個數多,單一消費者無法壓滿提供者 常規遠端服務方法呼叫
rmi 多連線 短連線 TCP 同步傳輸 Java 標準二進位制序列化 傳入傳出引數資料包大小混合,消費者與提供者個數差不多,可傳檔案。 常規遠端服務方法呼叫,與原生RMI服務互操作
hessian 多連線 短連線 HTTP 同步傳輸 Hessian二進位制序列化 傳入傳出引數資料包較大,提供者比消費者個數多,提供者壓力較大,可傳檔案。 頁面傳輸,檔案傳輸,或與原生hessian服務互操作
http 多連線 短連線 HTTP 同步傳輸 表單序列化 傳入傳出引數資料包大小混合,提供者比消費者個數多,可用瀏覽器檢視,可用表單或URL傳入引數 需同時給應用程式和瀏覽器 JS 使用的服務。
rest 多連線 短連線 HTTP 同步傳輸 表單序列化 同http,適用於更加符合rest規範的服務 同http

3.1.1 協議選擇 - dubbo

從使用場景上來看,dubbo協議是比較符合我們實際業務需求的,由於其資料包相較於Http協議體積小很多,傳輸速度也會更快,另外我們可以通過socket與provider建立長連線,可以減少反覆建連帶來的不必要的網路開銷.使用此協議,我們需要注意的幾個點是

  • 本地負載均衡策略
  • 與Provider建立TCP長連線
  • Hessian協議解析

小結 : 如果想要採用此協議連線Provider,可以使用dubbo官方推薦dubbo2.js

3.1.2 協議選擇 - rest

此協議是基於http,所以對consumer端就基本上沒有了限制.而且此協議我們在拼接好引數之後,可以直接通過瀏覽器或者是HTTP請求工具即可檢視結果,使用友好程度上來講,會優於dubbo. consumer端可以直接使用request庫請求,也不存在協議解析以及socket狀態維護的問題,消費端的程式碼實現難度也會比較小.使用此協議,我們需要注意的幾個點 :

  • 需要使用keep-alive來與provider保持建連,否則http反覆斷開重連會帶來很多不必要的網路開銷
  • 本地負載均衡策略
  • 此協議在dubbo 2.6.x+ 才支援

小結 : 效能上雖不及dubbo.但對開發人員相對友好,可以結合業務自身場景進行選擇.

3.1.3 補充

dubbo是支援一個服務以多種協議註冊,比如一個服務可以同時註冊dubbo://rest://,如果你想用http,但是目前公司所暴露出來的協議只支援dubbo,可以和提供服務的同學商量一下,額外再新增一個http協議成本也是能在接收範圍內的.

3.2 如何引用服務

目前引用服務有兩個方案,分別是

  • 直接引用
  • 通過註冊中心引用服務

3.2.1 直接引用服務

直接引用服務,顧名思義就是繞開註冊中心獲取我們所想要的服務提供者,由於繞開了註冊中心,自然也無法做到服務發現,而且由於單點問題,無法做到負載均衡以及高可用,所以生產環境不推薦使用此模式的

但是由於其開發上的便利性,在開發環境/測試環境仍可以嘗試使用此模式.

直連provider

由上圖所示,開發同學聯調過程中,需要在專案工程中對指定服務開發同學的機器進行直連,而其他沒有指定的服務將會預設走註冊中心.為了避免對工程程式碼的侵入性,我們會在工程中建立應對不同環境的dubbo.properies,而dubbo.properies不會加入到工程的版本控制當中,主要用於解決不同環境下的服務直連問題.其中服務的控制粒度可以精確到具體的服務.

直連程式碼片段

3.2.2 通過註冊中心引用服務

通過註冊中心發現引用服務,Dubbo常用的引用服務方式,可以做到服務自動發現,負載均衡.正式環境呼叫基本基於此模式.其中註冊中心實現有很多種,例如Zookeeper/Redis/Multicast.官方推薦Zookeeper.

通過註冊中心引用服務

3.3 服務請求結構的定義

服務請求體結構,是在對dubbo在註冊中心上註冊資訊的抽象之後的一層封裝,一方面可以提升開發人員的開發效率,另外降低開發人員自身手動拼接請求的錯誤率.

3.3.1 服務的構成

我們基於dubbo/rest兩種協議,來分析一下這兩種協議在註冊中心註冊包含哪些資訊.

  • dubbo : dubbo://192.168.1.2:10880/com.service.ProductService?dubbo=2.8&methods=getById,getByName
  • rest : rest://192.168.1.2:10081/service/com.service.ProductService?dubbo=2.8&methods=getById,getByName

我們對這兩個協議公共部分進行提取一下

  • protocol : 協議型別.例如 dubbo://.
  • host : provider主機地址
  • port : provider對外暴露服務的埠
  • interface : 對外暴露服務名稱,在java中是採用包名 + 服務名稱構成,例如com.service.ProductService
  • dubboVersion: dubbo版本
  • method:服務對外暴露的方法,一個服務會同時包含多個方法.
  • query:還有一個就是請求引數列表,此項是在java服務定義的,在註冊資訊中無法體現.

3.3.2 請求體的定義

基於上述服務結構構成的分析,dubbo和rest服務請求結構構成大體類似,我們對不同的協議請求的可以做如下定義.

// 1. dubbo協議的請求體定義
services.ProductService = (dubbo) => dubbo.proxyService({
    dubboInterface: 'com.service.ProductService',
    methods: {
        getById(id) {
            return [java.Long(id)];
        },
        getByName(name) {
            return [java.String(name)];
        }
    },
});
複製程式碼
// rest 請求體定義
services.ProductService = (dubbo) => dubbo.proxyService({
    dubboInterface: 'com.service.ProductService',
    methods: {
        getById(id) {
            return {
                method: 'get',
                query: [parseInt(id)]
            };
        },
        getByName(name) {
            return [String(name)];
        }
    },
});
複製程式碼

兩者最大不同點在於引數定義上的不同,dubbo需要強制轉換為強型別,而rest不需要.

3.4 服務定義的維護

我們在對服務定義完成之後,接下來就會面臨一個使用上的問題,最直接的方法就是為每個工程每個服務新建一個服務檔案,但是一用就會發現一個問題請求定義的檔案分散在不同工程,無法進行統一維護升級,維護成本較高.

維護成本

我們第一個反應是每個服務抽象出來,各自成為一個獨立的NPM包,譬如MemberService我們可以抽象成為@dubbo-service/member-service,這樣就可以解決檔案分散在不同工程導致的維護問題.

解決維護問題

3.4.1 後續問題

事情到這裡,我們已經解決了服務如何統一定義的問題,但是仍然沒有解決統一管理與維護的問題.如 :

  • 維護人員職責劃分問題.NPM包的維護工作該交給服務提供方還是服務呼叫方?
  • 專案遷移成本問題.如果涉及到專案遷移問題,可能會涉及到很多現有的Java服務初始化的問題,而手動去定義服務工程量巨大,如何降低遷移成本問題是我們不得不要面臨的問題.

4 最後

如果你覺得此篇文章對你有幫助,就順手點個贊吧~ 非常感謝

有什麼疑問可以直接評論回覆或者私信我,我會盡我所能回覆你~

相關文章