伺服器集群為什麼節點間通信為什麼要用到RPC,這個是為了解決什麼問題?

伺服器集群主節點將實現類註冊到ip+埠上,子節點通過訪問ip+埠+介面來獲取實現類,這種設計(遠端調用)是為什麼哈


rpc 是一件看起來 trivial 但做起來水深的事情。

服務間通信統一地使用 rpc 而非 adhoc 地通信,可以起到統一開發、上線、監控、trace、負載均衡等基礎設施的好處,可以得到一個明確的服務邊界。這裡面序列化就是一件很重要的事情,要在 payload 層面允許欄位的擴展,使用弱類型的 json 在這方面會很簡單,但是 thrift/protobuf 這類有 schema 的二進位方案更加可取,schema 會比文檔可靠得多。

對互聯網公司而言,尤其是對於成長中的互聯網公司,rpc 框架是服務化工作的一項基礎,而服務化作為一種分工方式,也是業內較為通行的實踐,不乏成功案例。

但是 rpc 通信一個比較危險的事情就是忘記了你在調用的是 rpc,仍保持著 in process 的調用模式不變。rpc 框架並不能 mask 住遠程過程調用的固有困難,過載、性能下降、宕機都是切切實實會發生的事情,這使得調用者與 rpc 之間的邊界會很難處理,尤其是複雜的業務場景,這時 rpc 應儘可能地粗粒度,儘可能地能大塊地處理掉一件完整的事情,縮小跨服務集成的界面。

這是 「服務化」 過程中容易踩到的一個坑。拆分出 rpc 並不是目標,真正重要的是去理解業務的邊界。rpc 的每一個介面在原則上都要保持向前兼容,如果介面過多,則很容易成為維護的負擔。尤其是對於快速迭代的業務,過早地拆分 rpc 並不是好事。這裡需要關心的事情是,接到一個完整的需求,能不能只修改一個服務或者少量集成就能完成?如果日常的需求迭代,要經常涉及到幾個 rpc 服務的級聯修改,就需要反思一下業務的邊界是不是有更合理的劃分方式。跨 rpc 的級聯修改很難自動化測試,往往也不可回滾,為了迭代效率和業務穩定性,要儘力避免這點。


說一下,在現今rpc不是一門值得提倡的技術。待會兒來解釋一下。

你的問題應該有兩個,

1 rpc是扮演什麼角色 ?

2 rpc集群為什麼把服務註冊到主節點 ?

rpc主要解決的一個痛點在於服務調用的高度透明化。

透明化是指隱藏socket通信細節,比如http這種是應用層透明,但是rpc站的高度比應用層更高,是代碼級的透明。

可以舉個栗子來說明,如果我們要調用基於http的restful服務,代碼基本如下:

request=httpclient.post("http://localhost/order/2016081511231", params);
body=request.body;
order=(Order)Json.toBean(body);
//......

那如果是rpc的化,代碼基本就變成了:

orderService=(OrderService)new OrderRemoteProxy();
Order order=orderService.getOrder(2016081511231);
//......

這麼一比較就很直觀了。rpc是站在比http更上層的調用,也就相當於多了一層封裝,僅此而已,下層可以依賴http、json、或者你自己定義的元數據協議,任何能清晰描述出「坐標、服務類、服務名、調用參數」的whatever都可以。多了這層封裝使我們在調用的時候不必關注這些底層細節。而rpc這層封裝其實技術含量很低,反射加強制轉換罷了。

但是你雖然在編程層面不用考慮了,不代表這個問題不存在了。假如我們在創業階段,只有兩台機器,一台機器192.168.0.11專門部署對外介面服務,一台機器部署服務來供調用者訪問,比如在192.168.0.75這台機器,那麼你在0.11遭遇的所有請求都直接路由到0.75這台機器就好了,好了,隨著我們的公司業務的正常開展,業務量擴大了,而且也賺了些錢,於是我們機房又多了一台機器0.76出現了。我們決定把新業務邏輯部署在這台機器上面,還是沒有問題,我們可以在工程中寫一個配置文件,哪些類是訪問0.75的,哪些類是訪問0.76的。然後業務量爆髮式增長,我們有了0.12,0.13,0.14這些機器可以部署對公服務,這些服務調用著0.75,0.76,0.77,0.78這些機器提供的業務邏輯,你發現每次要部署一個新服務,你就要把0.11、 0.12、 0.13、 0.14這些機器上面的工程中的配置文件加上新類的訪問路徑,終於有一天,你受不了了。於是單獨抽了一台機器0.56作為服務治理,用來集中管理這些零散在4台機器上面的各種服務。而前台服務0.1x都訪問這個0.56機器,0.56分析請求中的參數得到請求的方法,並生成全局唯一id,將這個guid作為key,這個socket為value,哈希起來,然後查詢路由表知道了具體是哪台機子提供這個方法的,於是把原始請求連帶這guid轉發給0.7x這台機器,0.7x這台機器處理完畢之後把response和guid一起返回給0.56 。0.56拿到應答之後獲得guid,通過哈希查找到具體是那個前台伺服器的socket句柄,通過這個socket回寫數據。於是架構從一堆亂麻的網狀連接變成了,0.1x的機器全部連接0.56這台機器,0.7x的機器也全部連接0.56這台機器,並且0.7x的機器在啟動的時候向0.56彙報自己能提供的服務列表,0.56會根據0.7x彙報的服務列表動態更新自己的轉發表。整個過程就變得非常清晰了。不知道在下這麼描述能不能解答第二個問題?

好,接下來,在下說說個人觀點,為什麼rpc不是一種值得發展和鼓勵的技術呢?

如今(2016年08月15日)信息技術的發展是越來越快,人們的生活和互聯網技術發生了緊密的結合,各種技術井噴般發展,這就像在下反覆在其他回答里提及的,技術只會越來越開放,一個百家爭鳴百花齊放的春秋盛世正在到來,這是任何人都阻擋不了。

rpc技術其實是這個趨勢的一大阻礙。因為一旦你到了代碼級這個高度了,也就被某一種技術,一種語言綁死了,並且rpc極力試圖將遠程調用包裝成本地調用在下認為這種設計思想本身就是錯誤的,網路調用的情況比本地調用要複雜的多,並且網路調用本身應該也是無狀態,冪等性的,和本地調用背離,隨便舉個例子,你在本地會自然而然的寫諸如i++,i--這種,但是在rpc上面不能這麼用,一旦網路情況複雜i++可能會被調用多次導致結果就不正確,而且經常會有把函數當做參數調用另一個函數的情況,比如回調,比如lambda,很自然,沒有這些基礎功能,一些編程模型和設計模式也就無法應用。所以把兩種根本不是一回事的調用混為一談的做法只是看上去編程簡單了,但是增加了編程時候的思考量,debug也會變得更加複雜,特別是處於複雜網路環境的語境下。反觀http,在下只需要一個瀏覽器甚至curl就可以debug了。而且旗幟鮮明的告訴別這裡就是一次網路請求!

而且編譯型語言還有一個問題就是需要欺騙編譯器,因為你要調用的服務是在遠程而不在本地,反序列化的對象通常也在遠程不在本地,Order order=OrderService.getOrder(xxxx),意味著等號左右兩邊都不在你的工程裡面,這下編譯器肯定不幹,你需要欺騙編譯器,至少把遠程依賴加進來,這個過程又容易搞出依賴風暴,依賴衝突。你會不勝其煩。從這一點上來說腳本語言有一個天生的優勢,就是duck type,它不需要欺騙編譯器這個動作,也就不需要依賴,讓rpc看上去要優雅不少,比如在下的娛樂作品https://github.com/nikoloss/pyfadeaway。

(PS:腳本語言本身擁有的出色的語法糖讓http訪問優雅如rpc,同樣又會讓rpc黯然失色。所以rpc的市場只會越來越窄。)

當然開源的rpc通常有一個解決方案就是通過描述文件生成骨架依賴(介面)和反序列化對象規則,這個額外的動作一是用來欺騙編譯器逃過編譯檢查,二是可以用來進行跨語言支持。

所以你看,在下覺得把簡單的問題弄複雜不是一個合格的程序員的追求,我們不能因為追求簡歷上一堆念出來就很牛逼的辭彙而使用技術。實際上我們用http+json/xml構建的restful服務不會比rpc多幾行代碼,而且有著更高的靈活性,稍微封裝一下,就可以達到rpc,並且json為代表的字元串協議足夠通用。是的,我們只需要做到應用層透明且協議足夠通用就OK了,任何人(部門、小組)只要有需要,他們可以自己在這一層協議上面封裝一層薄薄的rpc。又不會影響另一部分小組、部門的人開展新技術的預研和試水。

---------------------------update 2016-08-24

很多人誤會了在下。

其實在下也不是要鼓吹restful api,反對RPC,rest把任何東西都定義成resources本身也有一定局限性,遠程服務不一定都是資源類目同時也有動作行為method。所以在下想支持的本意應該是http api(包括在http json之上封裝的rpc)而不是restful api和純粹rpc。相比單純的rpc有著行為的確定性,例如head中的屬性可以由客戶端來設置字符集、是否緩存、緩存時間等等非常靈活通用。對新入職員工友好,一個瀏覽器就可以直觀的看到服務端吐出的數據是否有問題。這大大節約了程序員的生命。而人命關天嘛!(996,007的工地都應該燒掉)

------------------------------------------

#真心呼籲各工地架構師去rpc化。


RPC別人講的很明白了,我說點別的

先說明幾個前提

1 RPC沒有解決問題,而是保證其他方面複雜度可控的範圍內簡化了某些方面的問題。

2 RPC 是計算機 通訊 互聯網等相關行業發展與成本控制下的必然產物。

3 RPC沒有好和不好, 推薦和不推薦的說法,使用RPC是有範圍的

4 老話重提,軟體開發沒有銀彈(很多年沒人提起了)

5 在軟體開發這個層面上,沒有什麼新鮮東西。

最簡單的道理,最早我們通訊使用寫信交流,這個過程非常慢,但是協議很簡單,就是文字,載體是紙,渠道是郵遞員

和發個簡訊說不回家吃飯了也沒區別

這和通過json請求服務沒有區別,只是json表達更清晰,信號跑的比人快

所有的通信都只追求多個目標的平衡,也就是

1 使用簡單

2 實時性好

3 安全性高

4 穩定性強

5 表達清晰準確

將獲取服務和調用理解為信息的傳遞和處理的話,其實也在圍繞這幾個方面不斷平衡和進步。

所以在合適的場景下,你在這個場景的某個需求達到要求後仍然高度可控,資源充足,或者持續投入產出比降低時,就可以把額外的資源投入到提高其他幾個目標的指標上去,提高指標的過程中會額外引入新的複雜度影響到其他指標,直到平衡。

一個公司/團隊/組織,基於人員招聘 培訓 等成本考慮,會形成小範圍以某種語言為核心的技術簇

當這個技術簇使用RPC時候,RPC降低了溝通和開發的成本,而帶來的負面影響在團隊的接受和控制範圍之內,那麼RPC就是這個適用這個場景的。

比如 java作為現在技術簇巨頭之一,有不少一攬子開源解決方案,讓rpc的負面效果變得更加可控,所以java的使用rpc相對比較多。

對內rpc,對外rest,就是一個合理場景的合理選擇。沒什麼誰更好,只有誰更適合

至於那種方式更適合,寫代碼的管那麼多幹嘛。


每一種 RPC 實現可能都需要定義獨特的通訊協議與數據序列化協議,甚至需要某種語言特性的支持。相對於其它常用的通訊協議與數據序列化協議(如 HTTP restful + JSON)難以適應複雜的語言交互環境。

由於集群內部容易做到語言甚至框架層次的統一,而且 RPC 相對於 HTTP + URI 能夠提供更高的吞吐性能與更靈活的數據收發策略,所以 RPC 更適合集群內部通訊。

歡迎 C#er 關注 TCP 介面服務框架 - C# 高性能自動化服務端框架 - 凹凸架構。


RPC 的全稱是 Remote Procedure Call ,是進程間通信的一種方式,常見的分散式系統通信可以用http,socket或rpc來實現,但rpc相比於http性能更高,相比於純socket實現會更輕量更容易。目前比較火的微服務架構一般是基於rpc框架的,微服務是指開發一個單個小型的但有業務功能的服務,每個服務都有自己的處理和輕量通訊機制,可以部署在單個或多個伺服器上。

基於rpc的微服務架構優點如下:

  1. 每個微服務都很小,這樣能聚焦一個指定的業務功能或業務需求。

  2. 微服務能夠被小團隊單獨開發,這個小團隊是2到5人的開發人員組成。

  3. 微服務是松耦合的,是有功能意義的服務,無論是在開發階段或部署階段都是獨立的,單個服務方便擴展。

一般來講RPC主要分為四個部分,分別是序列化層、函數調用層、網路傳輸層和伺服器端處理框架,具體實現機制如下:

序列化層:序列化主要作用是將結構化對象轉為位元組流以便於通過網路進行傳輸或寫入持久存儲,在RPC框架中,它主要用於將用戶請求中的參數或者應答轉化成位元組流以便跨機器傳輸。常用的序列化方式有xml,json,hessian,pb等。

函數調用層:函數調用層主要功能是定位要調用的函數並執行該函數,可以採用Java反射機制與動態代理實現函數調用。

網路傳輸層:網路傳輸層描述了Client與Server之間消息傳輸的方式。

伺服器端處理框架:伺服器端處理框架可被抽象為網路I/O模型,它描述了客戶端與伺服器端間信息交互方式,它的設計直接決定著伺服器端的並發處理能力,常見的網路I/O模型有阻塞式I/O、非阻塞式I/O、事件驅動I/O等,而Hadoop RPC採用了基於Reactor設計模式的事件驅動I/O模型。

至於常見的rpc框架可以看看我的這篇blog http://my.oschina.net/iseeyou/blog/719173


主要是為了解決高並發,穩定性的問題!


在分散式系統中,設計者通常將RPC抽出來做成一個基礎組建即一個RPC框架,如hadoop內部有一個rpc框架。RPC框架會為用戶做大量的工作,使得網路通信和數據打包的過程對用戶是透明的。同時RPC框架還會做以下工作:

  1. 超時控制
  2. 數據壓縮
  3. 連接斷開重連
  4. 支持同步、非同步調用
  5. 支持多下游之間的負載均衡
  6. 支持流量控制
  7. 支持服務狀態實時統計
  8. 支持http訪問和json數據協議

詳細的東西可以參考百度開源的sofa-pbrpc GitHub - baidu/sofa-pbrpc: A light-weight RPC implement of google protobuf RPC framework.,百度開源的分散式表格系統和分散式文件系統都基於此實現。


伺服器集群主節點將實現類註冊到ip+埠上,子節點通過訪問ip+埠+介面來獲取實現類,這種設計(遠端調用)是為什麼哈

方便你有新服務上線,或者舊服務下線。

如果使用配置文件的方式,那有服務上下線那不是所有的client配置都要修改。


先問是不是,再說為什麼。

首先,RPC不是集群間通訊的唯一方式,此外還有message passing等等 。

其次,RPC是一種抽象,這個框裡面什麼都能裝,從重的不得了的CORBA,到後來的SOAP,再到xmlrpc再到rest,都是RPC。

然後,RPC也不是越輕越好,越輕,越難以對各種異常進行規範,也就越難做大。

手機碼字, 胡亂說說。


讓服務的訪問像空氣和水一樣自然


給你推薦一本書《分散式系統原理與范型》,裡面有一個章節就講了RPC。


說一下我們用的rpc。

對於SOA的web架構,rpc是一個重要的連接器。

服務化使各個服務解耦,獨立運維,開發視圖清晰,且可以方便的擴容縮容。

在我們業務里,基本承載了3個功能:

(1)透明訪問

簡化交互:支持多種消息格式,不管是 redis/cache/db/service, 均使用一致的訪問方式,開發簡便、配置統一、日誌統一

資源定位:分散式系統下,對單個服務擴容、縮容是常事,後端的部署節點變化時,調用方不需關注。

訪問優化:主要是機房偏好,提升訪問速度

(2)負載均衡 支持多種均衡策略,輪詢/權重/一致性哈希

(3)健康檢查 分散式系統下,單點故障是常事,當頻繁訪問某個後端結點失敗時屏蔽它(ps:這塊實際效果很差,沒做好)


所有非進程內調用都可以稱作rpc,一個稱呼而已


其實還是為了提升抽象層次,便於上層模塊站在更高位置上解決問題,防止腦爆。

沒有rpc的話,伺服器間交互那塊可能就會用一坨坨socket代碼來代替。

不過我不完全認同高票中rpc沒必要的觀點:

  1. 如果伺服器間交互簡單(如redis,1請求1應答這樣),是可以不用rpc的:可以手寫socket代碼,也可以復用各種規範協議。
  2. 但如果交互複雜(如各類分散式系統,網狀交互),是非常需要一個基礎rpc服務來提升抽象+優化交互性能的。


因為rpc簡單,很多東西它幫你實現了。

當然還可以用分散式計算框架,更簡單。


推薦閱讀:

分散式與集群的區別是什麼?

TAG:Java | 伺服器集群 | 集群 | ApacheStorm | 遠程過程調用協議RPCRemoteProcedureCallProtocol |