之前研究 Hadoop 源碼,把 hadoop-common 模塊下的 RPC 模塊源碼通讀一遍,又花了 3 個月抄了一遍 Hadoop RPC 代碼,學到很多東西。我覺得學習編程最有效的方式就是抄代碼,我覺得這個過程對正在學編程的朋友很有幫助,所以想做成教程,以下是周末寫的一個開頭,后續教程的形式以及進展會發布在公眾號,有興趣的朋友歡迎文末關注。
1. 起源
故事得從19年上半年說起,那時候我正打算研究一下Hadoop源碼。 現在大家都聽說過Hadoop,它是一個分布式存儲和計算的框架。作為分布式系統,節點之間的通信、交互式必不可少的。Hadoop自己實現了RPC(Remote Procedure Call,遠程過程調用)模塊來滿足這樣的需求。帶着好奇,我便閱讀了整個Hadoop RPC模塊的源代碼,讀完后發現這個模塊設計的非常好,與其他模塊無耦合,完全可以獨立出來當成一個獨立的框架。為了能夠學習相關的編程知識,同時還可以看到Apache 頂級開源項目的代碼如何編寫的,因此我便把Hadoop RPC模塊做成教程。
雖然這個項目是實現RPC功能,但我覺得我們重點不應該過多關注RPC本身,而應該重點學習RPC所涉及的客戶端開發、服務端開發、網絡編程、多線程、並發編程、設計模式等核心知識,尤其是對於剛學習Java沒有接觸線上實戰項目的朋友,掌握好了這些知識,寫其他項目也會更有思路。
2. 起名
為了能讓不熟悉Hadoop的朋友也能學習本教程,因此我們將Hadoop RPC賦予了新的業務含義。
假設我們有這樣一個場景,公司開發一個新的數據庫,這個數據庫的底層可能是Mysql,也可能是MongoDB,甚至可能是公司自研的數據庫技術,無論是什么,數據庫都可以作為服務端。作為用戶來說,數據庫底層用了什么技術並不關系,而是關心怎么使用。數據庫需要提供了API方便用戶調用,因此就需要有客戶端。
實現連接客戶端請求和服務端響應的技術就是RPC。我給這個項目起了一個名字叫Manis,那么,數據庫的名字便是ManisDb。工作中我們也比較鼓勵大家給自己的項目起個名字,有了名字,它就像自己的孩子一樣,我們會更有責任心把它做好。
3. 優勢
優勢都是相對的。Hadoop RPC 相比於一般的實戰項目來說,它是經過線上檢驗的,Hadoop集群規模最大達到上萬台,單一個RPC模塊完全可以獨立出來用於實戰。同時,我們還可以積累頂級開源項目的開發經驗,大到架構設計,小到設計模式,代碼規范。RPC在客戶端開發、服務端開發、網絡編程三方面都有涉及,且都是重點內容。
Hadoop RPC相比於源碼分析類的教程來說,優勢在於實戰意義比較強。我們會按照Hadoop RPC源碼把我們的野生項目Manis從0到1完整的敲一遍,還原度為95%。解釋下為什么不是100%,一方面為了突出重點,我會把不太重要、不是很核心的技術舍棄掉。另一方面為了符合新的業務定義,我會做一些改進,而不是照搬Hadoop RPC。比如:Hadoop RPC到了2.6版本只支持Protobuf序列化協議,但為了體現高擴展以及模塊間的低耦合,Manis支持了多種序列化協議。
在寫教程之前,我花了大概3個月時間先對照Hadoop RPC源碼把Manis敲出來了。學會了Manis后,你完全有能力閱讀Hadoop RPC的源碼,這也算是面試的加分項吧。可能有些讀者覺得這種方式比較LOW,但我還是相信馬化騰說的“抄代碼培養感覺”,寫代碼好比學字畫,不臨摹好的作品怎么學習別人的優點。真正要把看到的東西變成自己的,最終扎實的方式就是自己走一遍。至於Manis中被舍棄的部分我會在教程中說明,必要的時候會截取Hadoop源碼一起分析。
4. Manis架構圖
圖1-1是Manis的架構圖,基本上是一般的RPC架構圖。
圖1-1 Manis架構圖
5. Manis核心組件時序圖
圖1-2 Manis 核心組件時序圖
6. Manis核心組件概念
結合圖1-2對Manis中涉及的核心的組件概念進行說明。
ManisDb:圖中沒有表示,它代表數據庫,負責啟動服務端(即ipc.Server類),這里需要說明一下我們的重點在於RPC開發,這里的數據庫只是舉一個例子,不會涉及真正的數據庫開發。
ManisClient:提供給普通用戶的客戶端類,用於對ManisDb進行增刪改查,它使用Protobuf協議與ManisDb進行通信。
Manager:提供給管理員的客戶端類,用於對ManisDb進行管理,它使用Serializable協議(Java原生的序列化方式)與ManisDb進行通信
ProtoBufRpcEngine:支持Protobuf協議的RPC引擎,它定義了兩個內部類——Invoker類和ProtoBufRpcInvoker類。Invoker類用於封裝客戶端的調用請求,並使用Protobuf協議序列化。ProtoBufRpcInvoker類用於完成客戶端請求的方法調用(服務端調)。
SerializableRpcEngine:支持Serializable協議的RPC引擎,它定義兩個內部類——Invoker類和SerializableRpcInvoker類。Invoker類用於封裝客戶端的調用請求,並使用Serializable協議序列化。SerializableRpcInvoker類用於完成客戶端請求的方法調用(服務端調)。
Client:建立與服務端的socket連接,接收客戶端調用,並發送調用請求給服務端,等待服務端返回並將結果返回。
ipc.Server:該類定義在ipc包下面,通過Reactor模式接收並處理客戶端請求,最終調用ProtoBufRpcInvoker或SerializableRpcInvoker的方法獲得結果,並返回給客戶端。
公眾號「渡碼」,分享更多高質量內容