RPC - 麻雀雖小,五臟俱全

提及 RPC (遠程過程調用),你們應該不陌生。隨着微服務、分佈式愈來愈流行,RPC 應用愈來愈廣泛。常見的 RPC 框架如:Dubbo、gRPC、Thrift 等。本篇文章不是介紹各類 RPC 的使用和對比。而是深刻剖析一個 RPC 包含哪些內容。我最近在 Hadoop 的源碼,正好把 Hadoop RPC 看完了。感受 Hadoop 的 RPC 框架設計的仍是比價優秀的。Hadoop 做爲大數據技術的基石,若是沒有一個高性能、高可靠的 RPC 框架,很難支撐上千臺服務器規模的集羣。所以,本篇文章就以 Hadoop RPC 爲例,介紹一個 RPC 框架會涉及的技術。程序員

架構設計

RPC 的架構涉及客戶端、網絡、服務端三大組件。網絡通常使用 socket ,更多的是基於現有的網絡框架進行參數的設置達到最優的目的。可是客戶端和服務端須要咱們本身設計,而且對於分佈式框架來講,設計的架構應該有高性能、高可用以及可擴展的特色。編程

  • 高性能:因爲客戶端同時發起多個請求,這就要求系統可以快速處理,下降響應延遲。也就是高吞吐、低延遲。從客戶端角度來講,因爲建立客戶端到服務端的鏈接成本較高。所以能夠緩存鏈接資源,從而實現多個客戶端複用相同的鏈接資源,避免每一個客戶端都來建立而下降性能;從服務端角度來講,能夠啓動多線程來併發處理客戶端請求。除了多線程,能夠採用 Reactor 編程模式,提升多線程併發的性能。
  • 高可用:當咱們的服務端掛了,能不能有備用節點繼續提供服務。Hadoop 2.x 實現了 NameNode 的高可用。當客戶端須要經過 RPC 調用 NameNode 服務的過程當中,若是主 NameNode 宕機,那麼備用 NameNode 會升級成活動節點。同時會將 RPC 的請求發送的當前活躍的 NameNode,從而繼續提供可用的服務,而這個過程對客戶端來講是透明的。
  • 可擴展性:一個框架須要不斷地優化、不斷升級。須要在架構設計時明確不變的需求點,以及可變的需求點,對於可變的需求須要可以有良好的可擴展性。以 RPC 涉及的序列化爲例。因爲不一樣序列化框架適用場景不一樣,所以這須要被當成可變的需求點,應該將其設計成可擴展的,可以容易地支持不一樣的序列化框架。目前,Hadoop RPC 支持自身的序列化框架(Writable)和 Protoc Buffer。

設計模式

設計模式更多地與上面提到的可擴展性相呼應。良好的設計模式能夠提升代碼複用性、加強可擴展性,同時可以下降 BUG 數量。Hadoop RPC 中涉及的設計模式比較多,大概包括:工廠模式、代理模式、適配器模式、裝飾者模式和命令模式等。以代理模式爲例,當客戶端調用遠程方法時,其實是經過代理,將方法名和參數經過網絡發送到服務端。但這個過程對客戶端是透明的,對於客戶端來講就像調用本地方法同樣。設計模式

除了設計模式,在工程實踐中還應該注意遵循常見的設計原則。緩存

多線程

在任何一個系統中多線程都比較常見。經過多線程併發處理,提升系統的吞吐量。在 Hadoop RPC 中,客戶端與服務端都用到了多線程技術。客戶端開啓多線程,每一個線程處理一類請求,而且緩存鏈接資源。服務端也是多線程併發處理客戶端的請求,使用 Reactor 編程模式提升併發性能。安全

談到多線程就不得不提另外一個話題 —— 線程安全。Hadoop RPC 中用了很多的技術來保證線程安全,包括:synchronized、concurrent併發包、atomic併發包和 nio 工具包。從優秀框架中學習線程安全,對咱們之後併發編程有很多好處。服務器

序列化與反序列化

因爲 RPC 涉及數據在網絡上傳輸,所以須要一個優秀的序列化框架,既可以高效的編碼與解碼,且編碼後的數據大小又儘量小。不一樣的序列化框架主要是在編解碼效率和編碼大小兩個主要方面作權衡。Hadoop RPC 目前支持兩種序列化框架,一個是 Hadoop 本身實現的 Writable 框架,另外一個是 Protocol Buffer。Hadoop RPC 雖然支持 Writable 序列化框架,但仍是以 Protocol Buffer 爲主。由於 Protocol Buffer 從編解碼效率和編碼大小方便都是比較優秀的。固然常見的序列化包括 Avro、Kryo 等,有興趣的讀者能夠查一下它們之間的性能對比。網絡

其餘

一個 RPC 框架,除了包含上面提到比較主要的方面。還有一些其餘的方面多線程

  • 語言層面:利用好 Java 語言的繼承、組合、封裝、多態等特性。甚至包括泛型、註解等。
  • 代碼規範:良好的工程實現應該有一個良好的代碼規範。在 Hadoop 中,代碼風格比較統一,且每一個重要的類都有詳細的註釋,在關鍵的方法或者屬性上也有明確的註釋。我在本身的工程中會使用阿里的 Java 代碼規約插件,也會爲了讓本身的代碼更規範。
  • 異常處理:對於一個優秀的框架異常處理很關鍵,何時須要拋出異常、拋出什麼樣的異常以及何時須要處理異常。在 RPC 中除了須要處理本地異常還要處理遠程服務的異常。所以,在程序中如何優雅的處理異常也是體現一個程序員能力的地方。
  • 網絡編程:RCP 中涉及的網絡編程通常用 socket,Hadoop RPC 使用的 Reactor 模式的網絡編程,而且 Netty 也在使用這種框架。咱們有必要會用而且掌握它。

這一段寫的比較雜,想到哪寫到哪。最近有跟朋友聊過在看 RPC 相關的東西,朋友說:「一個 RPC 可以涉及多少東西?值得研究?」。其實我一開始也是這樣想的,無非就是客戶端將請求序列化,經過網絡發給服務端,服務端反序列化調用函數後再返回。可是看了 Hadoop RPC 代碼後,我發現這樣框架涉及的知識仍是特別多的,而且還比較系統,基本上包含了咱們平時編程涉及的方方面面。同時它再也不是一個單機程序,而是一個 C/S 架構的程序。若是咱們有興趣還能夠繼續研究他的高可用,從而對分佈式應用有更深刻的瞭解。架構

我以爲 RPC 是麻雀雖小五臟俱全。因爲它涉及了咱們編程的方方面面,因此我想基於 Hadoop RPC 作一個詳細的教程,把它涉及的每一個重要部分都進行詳細的分析,上面提到的內容基本都會涵蓋。對於想了解 RPC 的讀者,可以感覺到一個 RPC 框架更清晰的面貌。對於僅有 Java 基礎的讀者來講,可以學到編寫一個框架所涉及的具體編程技術,同時可以從世界頂級開源項目學到優秀設計和工程經驗。併發

小結

本篇文章主要介紹了 RPC 框架涉及的知識。包括:架構設計、設計模式以及設計原則、多線程併發以及線程安全、序列化框架和一些其餘的內容。我以爲學習最好的方式就是從優秀的框架中學習、模仿。比如咱們練書法基本都要通過臨摹這一步。固然直接看別人的代碼確實需求花費更多的時間和經歷,而且有時候投入與產出並不成正比。因此,我想把我在 Hadoop RPC 框架中學到的優秀的設計和實現可以整理成教程,以便有興趣的讀者學習。若是有任何建議歡迎與我交流。公衆號有福利

公衆號「渡碼」

相關文章
相關標籤/搜索