用隧道協(xié)議實現(xiàn)不同dubbo集群間的透明通信
前言
筆者最近完成了一個非常有意思的隧道機制(已在產(chǎn)線運行),可以讓注冊到不同zookeeper之間的dubbo集群之間能夠正常進行通信。如下圖所示:
例如圖中A/B兩個網(wǎng)絡(luò)隔離的集群,兩者只能通過專線進行通信。但是對于在里面的應(yīng)用來說,調(diào)用另外一個集群的dubbo服務(wù)(例如app1調(diào)用app3)依舊和原來的方式一模一樣,無需做任何修改。這個特性對于新建單元(機房),業(yè)務(wù)網(wǎng)絡(luò)隔離等場景非常有用。
本文就稍稍聊一下這個機制。
場景
這個dubbo集群通信機制,可被用在下面的場景中。
新建機房
在我們新建一個機房的過程中。正常情況下,需要將一整條鏈路的所有應(yīng)用以及相關(guān)設(shè)施全部部署到新的機房中。如下圖所示:
而在筆者新的機制中,如果本集群沒有對應(yīng)的接口,會去尋找有對應(yīng)接口的集群,就算其中缺失了一些系統(tǒng),整個機房依舊能夠work,將新建機房變?yōu)榭傻降?。大幅度減少了新建機房的復(fù)雜性。
新建業(yè)務(wù)單元
由于單機房機架位的限制或者一些其它原因,有一些業(yè)務(wù)希望剝離到一個單獨的單元(機房里面)。但是業(yè)務(wù)確需要一大堆原來單元的基礎(chǔ)服務(wù)。而不同單元之間的網(wǎng)絡(luò)又無法打通(安全性要求)。
如果按照傳統(tǒng)的模式,勢必要對業(yè)務(wù)系統(tǒng)做改造,例如建立一個業(yè)務(wù)網(wǎng)關(guān)來負責(zé)和基礎(chǔ)系統(tǒng)的通信,這個網(wǎng)關(guān)明顯費時費力而且沒什么技術(shù)含量,例如在業(yè)務(wù)代碼中將dubbo調(diào)用強行轉(zhuǎn)換為對業(yè)務(wù)網(wǎng)關(guān)的http調(diào)用,如下圖所示:
而且,每增加一個接口調(diào)用,都得在業(yè)務(wù)網(wǎng)關(guān)中轉(zhuǎn)換一把,添加對應(yīng)的接口包,然后發(fā)布。這樣的網(wǎng)關(guān)維護起來肯定是個天坑!隨著日益嚴格的安全性要求,不同業(yè)務(wù)間的網(wǎng)絡(luò)隔離要求會與日俱增。
筆者是搞中間件的,堅信做的基礎(chǔ)服務(wù)能夠?qū)I(yè)務(wù)透明,讓其感知不到才是一個好的設(shè)計。一旦需要業(yè)務(wù)大量配合這種由基礎(chǔ)架構(gòu)變更而引起的改造,無疑是非常的不友好,甚至是個失敗的設(shè)計。
故障隔離
事實上,筆者搞這一套隧道機制的初衷還有很大一部分原因是故障隔離。例如,筆者遇到數(shù)次由于業(yè)務(wù)系統(tǒng)使用zookeeper不當,往zookeeper寫了一大堆數(shù)據(jù),從而讓整個集群陷入不可用的風(fēng)險。而新的機制,可以讓不同的業(yè)務(wù)注冊到不同的zookeeper,zookeeper掛了,也只是這個業(yè)務(wù)宕了,其它業(yè)務(wù)則不受影響。
事實上不僅為zookeeper,由于筆者對消息(例如activemq)也做了這一套類似的隧道機制。使得我們的整個業(yè)務(wù)能夠更好的進行故障隔離!
隧道機制
筆者這個機制的便利性在于對業(yè)務(wù)的侵入性很少。對于基礎(chǔ)集群的應(yīng)用甚至完全不需要做修改。為了達成這個需求,筆者引入了在網(wǎng)絡(luò)上非常常用的隧道概念(Tunnel),這個大家可能平時都接觸過,Vxlan這些網(wǎng)絡(luò)協(xié)議都用了隧道。
隧道穿透
我們先來看一下最基本的原理,在系統(tǒng)A通過Dubbo調(diào)用系統(tǒng)B的時候,在同一個集群中走的是dubbo協(xié)議。而跨集群的時候,筆者將dubbo原始比特流承載在http協(xié)議上,在專線上發(fā)出去。
由于在B系統(tǒng)看來,接收到的都是相同的byte流,其無法(也不用)區(qū)分到底是走了一層專線還是直接調(diào)用。所以B系統(tǒng)無需更改任何代碼。
隧道實現(xiàn)
那么,這個隧道具體是如何實現(xiàn),系統(tǒng)A又是如何知道需要本集群沒有對應(yīng)的接口,需要通過http隧道調(diào)用到另一個集群的呢?這就引入了我們的隧道網(wǎng)關(guān)。
這里的概念也是和網(wǎng)絡(luò)上的默認網(wǎng)關(guān)類似,如果本集群內(nèi)找不到對應(yīng)的接受者就投遞到一個默認的網(wǎng)關(guān),由這個隧道網(wǎng)關(guān)來替我們傳遞調(diào)用。
如何發(fā)現(xiàn)這個網(wǎng)關(guān)
為了充分利用dubbo接口的注冊發(fā)現(xiàn)機制,筆者將隧道網(wǎng)關(guān)也暴露為一個dubbo接口,其輸入和輸出分別如下所示:
// 隧道網(wǎng)關(guān)接口請求體class TunnelInterfaceReq { // dubbo元信息,例如具體調(diào)用接口信息 MetaData dubbo // 原始請求A調(diào)用序列化后的比特流 byte[] body;}// 隧道網(wǎng)關(guān)接口返回體class TunnelInterfaceResp{ // dubbo元信息 MetaData dubbo // 返回值調(diào)用序列化后的比特流,由另一個集群的對應(yīng)系統(tǒng)返回 byte[] resp;}
有了這個dubbo接口,我們就可以很容易的將數(shù)據(jù)傳送給默認網(wǎng)關(guān)了。
注意,這里其實也是做了一層隧道協(xié)議,即用dubbo協(xié)議承載dubbo協(xié)議,用這種類似套娃的方法有效的利用了dubbo本身的注冊發(fā)現(xiàn)機制。
網(wǎng)關(guān)和網(wǎng)關(guān)之間通過http通信
由于不同集群之間通過專線進行通信,所以筆者采用了http通信來進行。在App1的請求到達隧道網(wǎng)關(guān)后,網(wǎng)關(guān)會將原始body比特流從TunnelInterfaceRequest中取出。然后放到一個http的請求中進行傳遞。如下圖所示:
值得注意的是,由于傳遞的是byte流,沒有攜帶任何業(yè)務(wù)信息(例如類型信息等),所以我們的隧道網(wǎng)關(guān)可以對任意dubbo請求進行隧道傳輸,而不像傳統(tǒng)的網(wǎng)關(guān)那樣需要添加各種業(yè)務(wù)對應(yīng)的jar包并不停發(fā)布-_-!
在圖中,投遞到另一端的隧道網(wǎng)關(guān)后,其從http協(xié)議中取出調(diào)用元信息和原始調(diào)用byte流,通過調(diào)用元信息找到App2。然后給App2重放byte流,這樣就可以進行dubbo調(diào)用了。事實上,App2從隧道網(wǎng)關(guān)看到的byte流和從集群內(nèi)其它機器調(diào)用的byte流完全一致。如下圖所示:
返回值也通過隧道機制
很明顯的,我們的返回值也需要通過隧道機制。和Request一樣,其也會走兩次隧道協(xié)議,如下圖所示:
那么App1真正接收到的其實是Tunnel Response,怎么讓其透明的接收原始response比特流呢?這就需要調(diào)用方接入筆者研發(fā)的輕量級jar包(其實,一開始的request的隧道也需要這樣的jar包)
對dubbo進行擴展
由于dubbo有非常優(yōu)秀的filter機制,可以在各種地方可以擴展。為了這個隧道機制,筆者就擴展了其中的invoke調(diào)用邏輯。如下圖所示:
只要引入筆者寫的jar包,就能夠非常輕松的進行自動擴展,除了pom.xml加兩行,其它業(yè)務(wù)代碼完全無需修改。
隧道網(wǎng)關(guān)的接口發(fā)現(xiàn)
那么隧道網(wǎng)關(guān)A是怎么知道接口在集群B,從而投遞給隧道網(wǎng)關(guān)B的呢?很明顯的,我們需要隧道網(wǎng)關(guān)間的集群通信機制。
例如,由隧道網(wǎng)關(guān)向其它不同的隧道網(wǎng)關(guān)詢問是否有此接口,并按一定策略做緩存即可。
dubbo集群的發(fā)現(xiàn)
最后的問題就是隧道網(wǎng)關(guān)怎么知道其它的dubbo集群的了,由于相對于dubbo接口數(shù)量,集群的數(shù)量是很少且不經(jīng)常改變。我們只需要找個地方簡單的記錄下即可,例如放到數(shù)據(jù)庫里面。然后由于是http調(diào)用,直接通過DNS解析域名即可做負載均衡。

性能
由于筆者的這套機制序列化和反序列化完全在Provider/Consumer端,完全沒有對網(wǎng)關(guān)形成任何壓力,所以網(wǎng)關(guān)的CPU消耗很低。在單個調(diào)用延遲上,由于多了兩跳,不可避免的有所損耗,大概每個接口多了2ms左右。
總結(jié)
這套機制從一開始構(gòu)想,到完全能夠在產(chǎn)線運行,并且性能損耗還很小,筆者還是花費了不少的精力??吹竭@樣的結(jié)果,還是非常有成就感的。事實上,這套隧道機制在非常多的地方借鑒了網(wǎng)絡(luò)上的概念。可謂它山之石可以攻玉!不同技術(shù)之間確實可以相互遷移,他們只是在不同的層級上解決了本質(zhì)相通的問題!
本文轉(zhuǎn)載自微信公眾號「解Bug之路」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系解Bug之路公眾號。