RPC 使用中的一些註意點

来源:http://www.cnblogs.com/mindwind/archive/2016/12/25/6220492.html
-Advertisement-
Play Games

最近線上碰到一點小問題,分析其原因發現是出在對 RPC 使用上的一些細節掌握不夠清晰導致。很多時候我們做業務開發會把 RPC 當作黑盒機制來使用,但若不對黑盒的工作原理有個基本掌握,也容易犯一些誤用的微妙錯誤。 雖然曾經已經寫過一篇 "《RPC 的概念模型與實現解析》" 從概念模型和實現細節上講述了 ...


最近線上碰到一點小問題,分析其原因發現是出在對 RPC 使用上的一些細節掌握不夠清晰導致。很多時候我們做業務開發會把 RPC 當作黑盒機制來使用,但若不對黑盒的工作原理有個基本掌握,也容易犯一些誤用的微妙錯誤。

雖然曾經已經寫過一篇《RPC 的概念模型與實現解析》 從概念模型和實現細節上講述了 RPC 的原理,這一篇就從使用上的一些註意點來捋一捋吧。

同步

RPC 的調用通常為了方便使用,會被偽裝成普通方法調用的形式。但實際二者之間存在巨大的差異,進程內的方法調用的時間量級是 ns(納秒),而進程間的 RPC 方法調用時間量級通常是 ms(毫秒),它們之間差著 10 的六次方呢。RPC 的冰山底部透視圖如下:

但在目前流行的微服務架構模式下,跨服務的同步調用隱藏著巨大的風險。一般微服務化架構下,通常一個業務的調用會跨 N(N 一般大於 2) 個服務進程,整個調用鏈路上的同步調用等待的瓶頸會由最慢(或脆弱)的服務決定,A-B-C 像這樣一個鏈路,A 同步調用 B 並等待返回,B 同步調用 C 並等待返回,以此類推,就像一組齒輪鏈,級級傳動,這很容易產生雪崩效應。若 C 服務掛住了,會導致前面的服務全部都因為等待超時而占用大量不必要的線程資源。

因此,微服務架構下,內部主服務鏈之間的 RPC 調用需要非同步化,服務之間的調用請求和等待結果相互之間解耦,如下是一個服務鏈路調用的示意圖:

外部用戶通過服務網關(API Gateway)發起調用並等待結果,隨後網關派發調用請求給後續服務,其主調用鏈路為 A-B-C,其內部為非同步調用,鏈路上不等待,最後由 C 返回結果給服務網關。其中 B 又依賴兩個子服務,S1 和 S2,B 需要 S1 和 S2 的返回結果才能發起 C 調用,因此在支線上 B 針對 S1 和 S2 調用就需要是同步的。

非同步

RPC 的同步調用確保請求送達對方並收到對方響應,若沒有收到響應,框架則拋出 Timeout 異常。這種情況下調用方是無法確定調用是成功還是失敗的,需要根據業務場景(是否可重入,冪等)選擇重試和補償策略。

而 RPC 的非同步調用意味著 RPC 框架不阻塞調用方線程,調用方不需要立刻拿到返回結果,甚至調用方根本就不關心返回結果。RPC 的非同步交互場景示意圖如下:

在上面的示意圖中,對於是否需要返回值的非同步請求,其中的細微差異在於是否返回一個 Future 對象給調用方,以便未來(Future)調用方可以再通過它來獲取返回值。正是因為這種 Future 機制的存在,所以針對前面(圖2)中 S1 和 S2 的調用就可以採用一種非同步並行的調用機制來提升並行性和性能,如下圖所示:

這樣調用 S1 和 S2 的總時間就由最慢的一個服務響應時間來決定了。(上圖中其實調用 S1 和 S2 不可能做到同時,有細微的時間差異,但相對跨進程的調用本身來說這種差異基本忽略不計。)

線程

RPC 的線程模型一般如下所示:

其中,RPC 的網路層通常採用非阻塞型 I/O 模型,放在 Java 的實現語境下就是 NIO 了。而 RPC 框架通常共用一個 I/O 線程池,處理所有連接上的 I/O 事件派發。通常業務事件會派發到內部的一個固定大小(可配置)的業務執行線程池,再由業務執行線程調用應用實現層的代碼。

但有些 RPC 框架在實現客戶端的 I/O 線程模型時,也採用了針對每個不同的服務端一個獨立的 I/O 線程池,這樣就變成了下麵這個圖所示:

這帶來了一個潛在的問題,在一個客戶端需要連接大量服務端時(這在基於 RPC 實現的服務框架中很常見),客戶端的 I/O 線程池數就和需連接的服務數相等。在現在的微服務部署模式下,一般一個服務部署在一個 Docker 容器中,同一個服務會有很多個(幾十上百個)進程共同組成集群提供服務,這樣就導致客戶端 I/O 線程數可能會很多。

而在 Docker 環境下 Java 的 Runtime.availableProcessors() 獲取的 CPU 數量實際是物理機的,而不是 Docker 隔離的核數。另外,像 Netty 這樣的網路框架經常預設是基於 CPU 核數來啟動預設的 I/O 線程數的,所以導致針對每個服務的客戶端會啟動 CPU 核數個 I/O 線程再乘上服務實例數,這個線程數量也是頗為客觀,出現單進程好幾千固化的線程,線程調度和切換的成本頗高,另外服務的水平擴展性也有一定的受限。這也是需要註意的另一點。

...

在曾經那篇《RPC 的概念模型與實現解析》 的的結尾,我曾寫到:

無論 RPC 的概念是如何優雅,但是“草叢中依然有幾條蛇隱藏著”,只有深刻理解了 RPC 的本質,才能更好地應用。

所以這一篇大概就是抓出了幾條隱藏著的蛇吧。


寫點文字,畫點畫兒,記錄成長瞬間。
微信公眾號「瞬息之間」,既然遇見,不如一起成長。




您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Python內嵌的集合類型有list、tuple、set、dict。 列表list:看似數組,但比數組強大,支持索引、切片、查找、增加等功能。 元組tuple:功能跟list差不多,但一旦生成,長度及元素都不可變(元素的元素還是可變),似乎就是一更輕量級、安全的list。 字典dict:鍵值對結構哈 ...
  • 流程式控制制 --道心 輸入用戶名,進行判斷 name=input("請輸入用戶名:") #值對比,記憶體地址對比(值相同,記憶體不必相同) if name =="daoxin": print("登陸成功") else: print("登陸失敗") 輸入用戶名和密碼進行判斷: import getpass ...
  • 信號與槽可以通過使用手寫代碼顯式的實現關聯 ,也可以運用 QMetaObject 類規定的槽 函數命名範式來實現自動關聯。 我們只需按照下麵的標準格式定 義槽函數,這之後,uic 將會根據 QMetaObject 類制定的規則,生成界面實體類的 setupUi() 函數的內容,並完成信號與槽的關聯... ...
  • 我們都知道,申請的資源,使用完畢後要釋放。但是這個釋放動作,一定要註意。 舉個例子,很多人動態分配的資源,在使用之後,往往直接調用了delete,而不管申請資源的時候用的是new還是new[]。 如下: #include using namespace std; int main(){ int *p... ...
  • XML.01-語法簡介 文檔聲明 元素(標簽) 屬性 註釋 特殊字元 CDATA區域 處理指令 啥是XML? xml (Extensible Markup Language)可擴展的標記語言,顧名思義,XML最重要的兩個因素: 可擴展 標簽,可以自定義的標簽 XML可以看作是HTML的一個超集,但是 ...
  • 本人起初是用Eclipse+Pydev學習python的,其實也覺得挺好用。不過後來因為同事推薦去試了下PyCharm,就一發不可收拾的愛上了。 嚴格來說,題目上的問題其實對於很多人都不算是問題,但是根據個人經驗,恐怕和我一樣野生自學的孩子們還真的有可能不知道,所以特別提一下。 問題現象: 在PyC ...
  • 使用Java應用程式發送E-mail十分簡單,但是首先你應該在你的機器上安裝JavaMail API 和Java Activation Framework (JAF) 。你可以在 JavaMail (Version 1.2) 下載最新的版本。你可以再 在JAF (Version 1.1.1)下載最新... ...
  • 閱讀目錄 前言 場景1的思考 場景2的思考 避坑方式 實踐 結語 一、前言 在上一篇中(如何一步一步用DDD設計一個電商網站(八)—— 會員價的集成),有一行註釋的代碼: 其中涉及的到問題是關於值對象的持久化問題。是的,由於我們之前的設計中持久化是僅針對聚合根的: 但是有時候難免會遇到一些需要持久化 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...