一、前 言
RPC 是遠程過程調用(Remote Procedure Call)的縮寫,它是一種通過網絡從遠程計算機程序上請求服務,而不需要了解底層網絡技術的協議。舉例來說,部署在 A 節點上的應用調用部署在 B 節點上的應用提供的接口,A 節點需要將調用的數據信息通過網絡傳遞到 B 節點,B 節點根據接收到的數據信息找到具體的接口執行,并將執行的結果通過網絡返回給節點 A。
RPC 框架封裝網絡傳輸、序列化、負載均衡、故障剔除等通用能力,使得 A 節點可以像調用本地方法一樣簡單地調用遠程接口。
SCF 是 58 自主研發的 RPC 框架,致力于在分布式環境下提供高性能、高可靠和透明化的 RPC 遠程調用方案。
服務管理平臺是基于 SCF 框架的服務治理平臺,具有服務節點自動注冊與發現、負載均衡、服務鑒權、全方位監控、完善的告警等特點。
二、整體架構

整體架構
- SCF 服務方: 指使用 SCF 框架服務端能力,提供可以被遠程調用的接口的應用。
- SCF 調用方: 指使用 SCF 框架客戶端能力,調用服務方提供接口的應用。
- 控制中心: 核心是維護 SCF 服務方和 SCF 調用方之間的調用關系,生成調用方需要使用的服務配置信息,支持當調用關系調整時,實時向調用方推送新的配置信息。
- 監控中心: 統一收集服務方和調用方的流量數據,并提供實時告警功能,可以提高業務人員對服務的整體把控能力,幫助服務負責人提高服務穩定性。
- 可視化管理平臺: 提供給業務的管理界面,可以查看服務方和調用方的流量監控數據、配置服務方和調用方的調用信息、設置豐富的告警等。
SCF 服務方和 SCF 調用方構成了 SCF 框架的主要組成部分,可以實現基本的 RPC 遠程調用。
控制中心、監控中心和可視化管理平臺三個部分屬于服務管理平臺,是對 SCF 框架基本能力的補充,對服務的治理提供了有效的手段。
三、SCF 框架
3.1SCF 調用模式

SCF 調用模式
RPC 框架最基本的能力是提供遠程調用,SCF 提供了同步調用和回調調用兩種調用模式。

SCF 調用模式
3.2同步調用
同步調用是業務使用最多的一種方式,也是框架默認的調用方式。調用方在調用服務的接口時,執行調用的線程會被阻塞,等待調用完成。如果服務方返回了結果或等待時間超過設置的超時時間,線程被喚醒,獲取返回結果或捕獲超時的異常。
3.3回調調用
回調調用是指調用服務接口之后,接口立即返回,調用接口的線程不需要等待服務端的返回結果,因此不存在阻塞的情況。如果服務端有返回結果或等待超過設置的超時時間,由框架中單獨的回調線程處理返回的結果或超時異常。因此在調用前必須設置接口的回調實現類。
3.4超時處理
在實際生產環境中,服務端方健康狀況不可控、網絡情況復雜可能出現各種異常情況。因此,上述同步或回調調用中,不是所有的調用都一定能得到服務方返回的結果,為了避免調用方無限制的等待,必須設置調用的超時時間。在超過設置的時間內沒有得到返回結果,就通過超時異常的方式通知調用方。
SCF 中使用經典的 TimeWheel 算法實現調用任務的過期。

TimeWheel
內部使用數組模擬類似時鐘的環形數據結構,每一個格子代表一個時間間隔,每個格子對應一個任務的鏈表,在添加過期任務時,通過過期時間和當前時間計算出任務應該在第幾個格子里并計算應該是走到第幾圈時觸發超時。
假設圖中每個格子表示 100ms,則一圈代表 800ms,當前是走到第 1 圈的第 2 個格子。如果任務 500ms 后超時,(500+200)% 800=7,因此將任務放到第 7 個格子對應的鏈表中,并標記第 1 圈超時。如果任務 1000ms 后超時,(1000+200)% 800=4,(1000+200)/800=1,因此將任務放入第 4 個格子對應的鏈表中,并標記第 2 圈超時。
上述過期算法存在有兩個關鍵點需要注意:
- 過期時間存在誤差,誤差范圍是每個格子代表的時間。
- 掃描任務過期的線程應該和執行過期操作的線程獨立,避免執行過期操作影響到后續任務的過期掃描。
3.5序列化
在網絡中傳輸的數據只能是 0 和 1 組成的二進制數據,而通常我們請求的數據信息是面向對象中具體類的對象,序列化就是實現對象的狀態信息轉換為可以存儲或傳輸的形式的過程,反序列化是序列化的逆過程。
SCF 框架采用了自定義的序列化實現方式,下面主要介紹序列化是如何實現非對稱序列化和泛型序列化。
3.5.1非對稱序列化
互聯網是一個變化非常快的行業,在發布一個接口之后,隨著業務的發展必然會產生對接口傳輸對象進行調整的情況,因此就有了增加或刪除類中的成員變量的需求。如果不能支持服務方和調用方的類存在非對稱的成員,業務升級將會非常麻煩。
SCF 序列化對非對稱類處理的思想是對類的成員變量進行編號,在寫數據流的過程中,成員變量根據編號 (id)+ 數據長度 (length)+ 數據 (value) 的方式依次寫入二進制流,反序列化則從流中先讀取 id,判斷需要賦值的類是否存在該 id 的成員,如果存在繼續讀取長度和數據部分,如果不存在該 id,則根據讀取的長度跳過二進制流中該 id 成員對應的數據部分,從而實現忽略不存在成員的目的。

image
針對以上兩個版本的實體,左邊是編號 1、2、3 的成員,右邊是 1、4 的成員。序列化和反序列化過程如下:

image

image
使用基本的 id + length + value 的方式可以實現非對稱序列化,但是對所有的成員都需要寫入 id 和 length 兩個特殊的標識,增加了二進制數據的大小。而對于基本類型,其實長度是已知的。通過對數據類型按下面 type 進行劃分:

image
只需要 3bit 就可以表示說要的數據類型,因此采用 tag = (id << 3)|type 的方式,將 type 嵌入到 tag 字段中,實現基本類型的數據只需要寫入 tag 數據,不需要寫入 length 字段,有效減少二進制數據大小。
3.5.2泛型序列化
泛型序列化是指在類中存在非具體類型成員變量(JAVA 中的基類 Object)的對象序列化。
SCF 中使用全限定類名 hash 的方式,為每一個類生成唯一 typeId,在寫入泛型成員時,先寫入類的 typeId,再寫入 value 數據。讀取時一樣通過先讀取 typeId,查找具體類型,再根據類型讀取 value 數據。
四、服務注冊與發現
調用方通過網絡調用服務方,必須要知道服務方節點的 IP 列表,才能發起調用。最原始的方式是通過在調用方使用配置文件的方式指定,但是這種方式在實際使用中不能動態感知服務方節點的變化,不夠靈活也無法時間服務的自動化擴縮容。
服務注冊與發現即自動發現服務的節點信息,并且調用方能及時感知服務方節點的變化情況,自動調整流量切換到新的節點。

image
SCF 使用 ETCD 集群管理服務節點,每一個服務節點對應 ETCD 中的一個 key,并且為 key 設置一個 TTL 過期時間。通過心跳刷新 TTL 的方式維持服務節點在線狀態。為隔離 ETCD 集群與業務部署環境,避免服務節點的增加造成 ETCD 集群的連接數過高等問題,封裝了一層服務管理節點做代理,轉發服務心跳并維護服務方和調用方的狀態信息。

image
當服務節點下線,ETCD 集群通知服務管理節點對應的 key,服務管理節點實時推送最新的服務節點列表信息給調用方,調用方動態更新并切換流量。同時為了兼容推送失敗的異常情況,增加了調用方定時根據時間戳校驗拉取的策略,保證服務節點信息的最終一致。
五、監控數據采集與存儲
服務在生產環境運行是否正常?當前服務流量是多少?有沒有出現調用異常或超時的情況?這些都是服務的負責人需要關注的問題。
5.1數據采集
對于服務方來說,一個服務有多個方法,同時部署在多個節點上,同時會被不同的調用方調用不同的方法。同樣一個調用方也會同時調用多個服務的不同的方法。導致整體的收集維度是服務方和調用方的乘積量級,應該如何有效采集數據呢?
下面給大家介紹一下針對 58RPC 框架的調用數據的采集方案。

image
從總的架構圖中可以看到,為了避免流量數據收集的壓力,盡可能充分利用各層的計算能力分攤統一匯總的壓力。
- 收集插件充分利用服務節點的計算能力,先進行本地數據聚合,以分鐘為單位進行數據上報。
- 插件上報根據服務名 hash,盡可能保證相同服務不同節點的數據發送到同一個收集服務器,收集服務器再進行一次聚合,進一步減少 Cache 統一計數的壓力。
5.2數據存儲
首先針對服務的調用信息,我們來看一下針對一個調用需要存儲的數據情況。

image
對于同一個維度的監控數據,以上字段中只有時間戳、次數和耗時數據是和實際的流量相關的,服務名 + 服務節點 + 函數名稱 + 調用者 + 類型標識對同一個維度是相同的,因此為了減少數據的存儲,我們定義一個映射的規則(S[demo]SN[10.0.0.1]SF[Service.get()]C[callerdemo] 表示服務方 demo 的 10.0.0.1 機器上的 Service.get() 方法被調用方 callerdemo 調用),將以上 5 個收集元信息映射成唯一的維度字符串,再把所有的維度字符串分別生成一個唯一的 cid,實際存儲的監控數據中使用 cid 替換以上 5 個收集元信息。

image
在實際的應用中,最開始版本只存儲調用的元數據,在展示的時候根據展示的維度進行數據查詢聚合導致監控數據展示特別慢,因為需要經過大量的數據查詢和合并,為了調高監控數據的查詢速度,使用了寫擴散的方式,針對一個調用元數據,做如下圖所示的擴散:

image
從上圖可以看出,實際存儲的時候將未來經常需要展示的數據先計算好直接存入庫中,展示的時候只需要直接根據維度的 cid 直接查詢結果即可,有效提高了查詢速度。
六、總 結
SCF 框架作為 58 分布式架構的基礎組件,支撐了 58 集團內部萬級別節點的網絡調用。本文主要介紹基本調用和監控相關內容。還有很多負載均衡、網絡管理、故障節點剔除、服務鑒權、服務限流等模塊沒有展開。SCF 框架經過多次的迭代,從最初的最簡單的遠程調用到現在服務治理周邊功能的完善,后續也將不斷優化,歡迎感興趣的同學一起溝通交流。
作者:愛情小傻蛋
鏈接:https://www.jianshu.com/p/d02022f35f94
來源:簡書