一、前 言
RPC 是遠(yuǎn)程過(guò)程調(diào)用(Remote Procedure Call)的縮寫,它是一種通過(guò)網(wǎng)絡(luò)從遠(yuǎn)程計(jì)算機(jī)程序上請(qǐng)求服務(wù),而不需要了解底層網(wǎng)絡(luò)技術(shù)的協(xié)議。舉例來(lái)說(shuō)观腊,部署在 A 節(jié)點(diǎn)上的應(yīng)用調(diào)用部署在 B 節(jié)點(diǎn)上的應(yīng)用提供的接口,A 節(jié)點(diǎn)需要將調(diào)用的數(shù)據(jù)信息通過(guò)網(wǎng)絡(luò)傳遞到 B 節(jié)點(diǎn)算行,B 節(jié)點(diǎn)根據(jù)接收到的數(shù)據(jù)信息找到具體的接口執(zhí)行梧油,并將執(zhí)行的結(jié)果通過(guò)網(wǎng)絡(luò)返回給節(jié)點(diǎn) A。
RPC 框架封裝網(wǎng)絡(luò)傳輸州邢、序列化婶溯、負(fù)載均衡、故障剔除等通用能力偷霉,使得 A 節(jié)點(diǎn)可以像調(diào)用本地方法一樣簡(jiǎn)單地調(diào)用遠(yuǎn)程接口迄委。
SCF 是 58 自主研發(fā)的 RPC 框架,致力于在分布式環(huán)境下提供高性能类少、高可靠和透明化的 RPC 遠(yuǎn)程調(diào)用方案叙身。
服務(wù)管理平臺(tái)是基于 SCF 框架的服務(wù)治理平臺(tái),具有服務(wù)節(jié)點(diǎn)自動(dòng)注冊(cè)與發(fā)現(xiàn)硫狞、負(fù)載均衡信轿、服務(wù)鑒權(quán)、全方位監(jiān)控残吩、完善的告警等特點(diǎn)财忽。
二、整體架構(gòu)
SCF 服務(wù)方: 指使用 SCF 框架服務(wù)端能力泣侮,提供可以被遠(yuǎn)程調(diào)用的接口的應(yīng)用即彪。
SCF 調(diào)用方: 指使用 SCF 框架客戶端能力,調(diào)用服務(wù)方提供接口的應(yīng)用活尊。
控制中心: 核心是維護(hù) SCF 服務(wù)方和 SCF 調(diào)用方之間的調(diào)用關(guān)系隶校,生成調(diào)用方需要使用的服務(wù)配置信息漏益,支持當(dāng)調(diào)用關(guān)系調(diào)整時(shí),實(shí)時(shí)向調(diào)用方推送新的配置信息深胳。
監(jiān)控中心: 統(tǒng)一收集服務(wù)方和調(diào)用方的流量數(shù)據(jù)绰疤,并提供實(shí)時(shí)告警功能,可以提高業(yè)務(wù)人員對(duì)服務(wù)的整體把控能力舞终,幫助服務(wù)負(fù)責(zé)人提高服務(wù)穩(wěn)定性轻庆。
可視化管理平臺(tái): 提供給業(yè)務(wù)的管理界面,可以查看服務(wù)方和調(diào)用方的流量監(jiān)控?cái)?shù)據(jù)敛劝、配置服務(wù)方和調(diào)用方的調(diào)用信息榨了、設(shè)置豐富的告警等。
SCF 服務(wù)方和 SCF 調(diào)用方構(gòu)成了 SCF 框架的主要組成部分攘蔽,可以實(shí)現(xiàn)基本的 RPC 遠(yuǎn)程調(diào)用。
控制中心呐粘、監(jiān)控中心和可視化管理平臺(tái)三個(gè)部分屬于服務(wù)管理平臺(tái)满俗,是對(duì) SCF 框架基本能力的補(bǔ)充,對(duì)服務(wù)的治理提供了有效的手段作岖。
三唆垃、SCF 框架
3.1SCF 調(diào)用模式
RPC 框架最基本的能力是提供遠(yuǎn)程調(diào)用,SCF 提供了同步調(diào)用和回調(diào)調(diào)用兩種調(diào)用模式痘儡。
3.2同步調(diào)用
同步調(diào)用是業(yè)務(wù)使用最多的一種方式辕万,也是框架默認(rèn)的調(diào)用方式。調(diào)用方在調(diào)用服務(wù)的接口時(shí)沉删,執(zhí)行調(diào)用的線程會(huì)被阻塞渐尿,等待調(diào)用完成。如果服務(wù)方返回了結(jié)果或等待時(shí)間超過(guò)設(shè)置的超時(shí)時(shí)間矾瑰,線程被喚醒砖茸,獲取返回結(jié)果或捕獲超時(shí)的異常。
3.3回調(diào)調(diào)用
回調(diào)調(diào)用是指調(diào)用服務(wù)接口之后殴穴,接口立即返回凉夯,調(diào)用接口的線程不需要等待服務(wù)端的返回結(jié)果,因此不存在阻塞的情況采幌。如果服務(wù)端有返回結(jié)果或等待超過(guò)設(shè)置的超時(shí)時(shí)間劲够,由框架中單獨(dú)的回調(diào)線程處理返回的結(jié)果或超時(shí)異常。因此在調(diào)用前必須設(shè)置接口的回調(diào)實(shí)現(xiàn)類休傍。
3.4超時(shí)處理
在實(shí)際生產(chǎn)環(huán)境中征绎,服務(wù)端方健康狀況不可控、網(wǎng)絡(luò)情況復(fù)雜可能出現(xiàn)各種異常情況磨取。因此炒瘸,上述同步或回調(diào)調(diào)用中淤堵,不是所有的調(diào)用都一定能得到服務(wù)方返回的結(jié)果,為了避免調(diào)用方無(wú)限制的等待顷扩,必須設(shè)置調(diào)用的超時(shí)時(shí)間拐邪。在超過(guò)設(shè)置的時(shí)間內(nèi)沒有得到返回結(jié)果,就通過(guò)超時(shí)異常的方式通知調(diào)用方隘截。
SCF 中使用經(jīng)典的 TimeWheel 算法實(shí)現(xiàn)調(diào)用任務(wù)的過(guò)期扎阶。
內(nèi)部使用數(shù)組模擬類似時(shí)鐘的環(huán)形數(shù)據(jù)結(jié)構(gòu),每一個(gè)格子代表一個(gè)時(shí)間間隔婶芭,每個(gè)格子對(duì)應(yīng)一個(gè)任務(wù)的鏈表东臀,在添加過(guò)期任務(wù)時(shí),通過(guò)過(guò)期時(shí)間和當(dāng)前時(shí)間計(jì)算出任務(wù)應(yīng)該在第幾個(gè)格子里并計(jì)算應(yīng)該是走到第幾圈時(shí)觸發(fā)超時(shí)犀农。
假設(shè)圖中每個(gè)格子表示 100ms惰赋,則一圈代表 800ms,當(dāng)前是走到第 1 圈的第 2 個(gè)格子呵哨。如果任務(wù) 500ms 后超時(shí)赁濒,(500+200)% 800=7,因此將任務(wù)放到第 7 個(gè)格子對(duì)應(yīng)的鏈表中孟害,并標(biāo)記第 1 圈超時(shí)拒炎。如果任務(wù) 1000ms 后超時(shí),(1000+200)% 800=4挨务,(1000+200)/800=1击你,因此將任務(wù)放入第 4 個(gè)格子對(duì)應(yīng)的鏈表中,并標(biāo)記第 2 圈超時(shí)谎柄。
上述過(guò)期算法存在有兩個(gè)關(guān)鍵點(diǎn)需要注意:
過(guò)期時(shí)間存在誤差丁侄,誤差范圍是每個(gè)格子代表的時(shí)間。
掃描任務(wù)過(guò)期的線程應(yīng)該和執(zhí)行過(guò)期操作的線程獨(dú)立朝巫,避免執(zhí)行過(guò)期操作影響到后續(xù)任務(wù)的過(guò)期掃描绒障。
3.5序列化
在網(wǎng)絡(luò)中傳輸?shù)臄?shù)據(jù)只能是 0 和 1 組成的二進(jìn)制數(shù)據(jù),而通常我們請(qǐng)求的數(shù)據(jù)信息是面向?qū)ο笾芯唧w類的對(duì)象捍歪,序列化就是實(shí)現(xiàn)對(duì)象的狀態(tài)信息轉(zhuǎn)換為可以存儲(chǔ)或傳輸?shù)男问降倪^(guò)程户辱,反序列化是序列化的逆過(guò)程。
SCF 框架采用了自定義的序列化實(shí)現(xiàn)方式糙臼,下面主要介紹序列化是如何實(shí)現(xiàn)非對(duì)稱序列化和泛型序列化庐镐。
3.5.1非對(duì)稱序列化
互聯(lián)網(wǎng)是一個(gè)變化非常快的行業(yè)变逃,在發(fā)布一個(gè)接口之后必逆,隨著業(yè)務(wù)的發(fā)展必然會(huì)產(chǎn)生對(duì)接口傳輸對(duì)象進(jìn)行調(diào)整的情況,因此就有了增加或刪除類中的成員變量的需求。如果不能支持服務(wù)方和調(diào)用方的類存在非對(duì)稱的成員名眉,業(yè)務(wù)升級(jí)將會(huì)非常麻煩粟矿。
SCF 序列化對(duì)非對(duì)稱類處理的思想是對(duì)類的成員變量進(jìn)行編號(hào),在寫數(shù)據(jù)流的過(guò)程中损拢,成員變量根據(jù)編號(hào) (id)+ 數(shù)據(jù)長(zhǎng)度 (length)+ 數(shù)據(jù) (value) 的方式依次寫入二進(jìn)制流陌粹,反序列化則從流中先讀取 id,判斷需要賦值的類是否存在該 id 的成員福压,如果存在繼續(xù)讀取長(zhǎng)度和數(shù)據(jù)部分掏秩,如果不存在該 id,則根據(jù)讀取的長(zhǎng)度跳過(guò)二進(jìn)制流中該 id 成員對(duì)應(yīng)的數(shù)據(jù)部分荆姆,從而實(shí)現(xiàn)忽略不存在成員的目的蒙幻。
針對(duì)以上兩個(gè)版本的實(shí)體,左邊是編號(hào) 1胆筒、2邮破、3 的成員,右邊是 1仆救、4 的成員抒和。序列化和反序列化過(guò)程如下:
使用基本的 id + length + value 的方式可以實(shí)現(xiàn)非對(duì)稱序列化,但是對(duì)所有的成員都需要寫入 id 和 length 兩個(gè)特殊的標(biāo)識(shí)派桩,增加了二進(jìn)制數(shù)據(jù)的大小。而對(duì)于基本類型蚌斩,其實(shí)長(zhǎng)度是已知的铆惑。通過(guò)對(duì)數(shù)據(jù)類型按下面 type 進(jìn)行劃分:
只需要 3bit 就可以表示說(shuō)要的數(shù)據(jù)類型,因此采用 tag = (id << 3)|type 的方式送膳,將 type 嵌入到 tag 字段中员魏,實(shí)現(xiàn)基本類型的數(shù)據(jù)只需要寫入 tag 數(shù)據(jù),不需要寫入 length 字段叠聋,有效減少二進(jìn)制數(shù)據(jù)大小撕阎。
3.5.2泛型序列化
泛型序列化是指在類中存在非具體類型成員變量(Java 中的基類 Object)的對(duì)象序列化。
SCF 中使用全限定類名 hash 的方式碌补,為每一個(gè)類生成唯一 typeId虏束,在寫入泛型成員時(shí),先寫入類的 typeId厦章,再寫入 value 數(shù)據(jù)镇匀。讀取時(shí)一樣通過(guò)先讀取 typeId,查找具體類型袜啃,再根據(jù)類型讀取 value 數(shù)據(jù)汗侵。
四、服務(wù)注冊(cè)與發(fā)現(xiàn)
調(diào)用方通過(guò)網(wǎng)絡(luò)調(diào)用服務(wù)方,必須要知道服務(wù)方節(jié)點(diǎn)的 IP 列表晰韵,才能發(fā)起調(diào)用发乔。最原始的方式是通過(guò)在調(diào)用方使用配置文件的方式指定,但是這種方式在實(shí)際使用中不能動(dòng)態(tài)感知服務(wù)方節(jié)點(diǎn)的變化雪猪,不夠靈活也無(wú)法時(shí)間服務(wù)的自動(dòng)化擴(kuò)縮容栏尚。
服務(wù)注冊(cè)與發(fā)現(xiàn)即自動(dòng)發(fā)現(xiàn)服務(wù)的節(jié)點(diǎn)信息,并且調(diào)用方能及時(shí)感知服務(wù)方節(jié)點(diǎn)的變化情況浪蹂,自動(dòng)調(diào)整流量切換到新的節(jié)點(diǎn)抵栈。
SCF 使用 ETCD 集群管理服務(wù)節(jié)點(diǎn),每一個(gè)服務(wù)節(jié)點(diǎn)對(duì)應(yīng) ETCD 中的一個(gè) key坤次,并且為 key 設(shè)置一個(gè) TTL 過(guò)期時(shí)間古劲。通過(guò)心跳刷新 TTL 的方式維持服務(wù)節(jié)點(diǎn)在線狀態(tài)。為隔離 ETCD 集群與業(yè)務(wù)部署環(huán)境缰猴,避免服務(wù)節(jié)點(diǎn)的增加造成 ETCD 集群的連接數(shù)過(guò)高等問(wèn)題产艾,封裝了一層服務(wù)管理節(jié)點(diǎn)做代理,轉(zhuǎn)發(fā)服務(wù)心跳并維護(hù)服務(wù)方和調(diào)用方的狀態(tài)信息滑绒。
當(dāng)服務(wù)節(jié)點(diǎn)下線闷堡,ETCD 集群通知服務(wù)管理節(jié)點(diǎn)對(duì)應(yīng)的 key,服務(wù)管理節(jié)點(diǎn)實(shí)時(shí)推送最新的服務(wù)節(jié)點(diǎn)列表信息給調(diào)用方疑故,調(diào)用方動(dòng)態(tài)更新并切換流量杠览。同時(shí)為了兼容推送失敗的異常情況,增加了調(diào)用方定時(shí)根據(jù)時(shí)間戳校驗(yàn)拉取的策略纵势,保證服務(wù)節(jié)點(diǎn)信息的最終一致踱阿。
五、監(jiān)控?cái)?shù)據(jù)采集與存儲(chǔ)
服務(wù)在生產(chǎn)環(huán)境運(yùn)行是否正常钦铁?當(dāng)前服務(wù)流量是多少软舌?有沒有出現(xiàn)調(diào)用異常或超時(shí)的情況牛曹?這些都是服務(wù)的負(fù)責(zé)人需要關(guān)注的問(wèn)題佛点。
5.1數(shù)據(jù)采集
對(duì)于服務(wù)方來(lái)說(shuō),一個(gè)服務(wù)有多個(gè)方法黎比,同時(shí)部署在多個(gè)節(jié)點(diǎn)上超营,同時(shí)會(huì)被不同的調(diào)用方調(diào)用不同的方法。同樣一個(gè)調(diào)用方也會(huì)同時(shí)調(diào)用多個(gè)服務(wù)的不同的方法阅虫。導(dǎo)致整體的收集維度是服務(wù)方和調(diào)用方的乘積量級(jí)糟描,應(yīng)該如何有效采集數(shù)據(jù)呢?
下面給大家介紹一下針對(duì) 58RPC 框架的調(diào)用數(shù)據(jù)的采集方案书妻。
從總的架構(gòu)圖中可以看到船响,為了避免流量數(shù)據(jù)收集的壓力躬拢,盡可能充分利用各層的計(jì)算能力分?jǐn)偨y(tǒng)一匯總的壓力。
收集插件充分利用服務(wù)節(jié)點(diǎn)的計(jì)算能力见间,先進(jìn)行本地?cái)?shù)據(jù)聚合聊闯,以分鐘為單位進(jìn)行數(shù)據(jù)上報(bào)。
插件上報(bào)根據(jù)服務(wù)名 hash米诉,盡可能保證相同服務(wù)不同節(jié)點(diǎn)的數(shù)據(jù)發(fā)送到同一個(gè)收集服務(wù)器菱蔬,收集服務(wù)器再進(jìn)行一次聚合,進(jìn)一步減少 Cache 統(tǒng)一計(jì)數(shù)的壓力史侣。
5.2數(shù)據(jù)存儲(chǔ)
首先針對(duì)服務(wù)的調(diào)用信息拴泌,我們來(lái)看一下針對(duì)一個(gè)調(diào)用需要存儲(chǔ)的數(shù)據(jù)情況。
對(duì)于同一個(gè)維度的監(jiān)控?cái)?shù)據(jù)惊橱,以上字段中只有時(shí)間戳蚪腐、次數(shù)和耗時(shí)數(shù)據(jù)是和實(shí)際的流量相關(guān)的,服務(wù)名 + 服務(wù)節(jié)點(diǎn) + 函數(shù)名稱 + 調(diào)用者 + 類型標(biāo)識(shí)對(duì)同一個(gè)維度是相同的税朴,因此為了減少數(shù)據(jù)的存儲(chǔ)回季,我們定義一個(gè)映射的規(guī)則(S[demo]SN[10.0.0.1]SF[Service.get()]C[callerdemo] 表示服務(wù)方 demo 的 10.0.0.1 機(jī)器上的 Service.get() 方法被調(diào)用方 callerdemo 調(diào)用),將以上 5 個(gè)收集元信息映射成唯一的維度字符串正林,再把所有的維度字符串分別生成一個(gè)唯一的 cid泡一,實(shí)際存儲(chǔ)的監(jiān)控?cái)?shù)據(jù)中使用 cid 替換以上 5 個(gè)收集元信息。
在實(shí)際的應(yīng)用中觅廓,最開始版本只存儲(chǔ)調(diào)用的元數(shù)據(jù)鼻忠,在展示的時(shí)候根據(jù)展示的維度進(jìn)行數(shù)據(jù)查詢聚合導(dǎo)致監(jiān)控?cái)?shù)據(jù)展示特別慢,因?yàn)樾枰?jīng)過(guò)大量的數(shù)據(jù)查詢和合并杈绸,為了調(diào)高監(jiān)控?cái)?shù)據(jù)的查詢速度帖蔓,使用了寫擴(kuò)散的方式,針對(duì)一個(gè)調(diào)用元數(shù)據(jù)蝇棉,做如下圖所示的擴(kuò)散:
從上圖可以看出讨阻,實(shí)際存儲(chǔ)的時(shí)候?qū)⑽磥?lái)經(jīng)常需要展示的數(shù)據(jù)先計(jì)算好直接存入庫(kù)中芥永,展示的時(shí)候只需要直接根據(jù)維度的 cid 直接查詢結(jié)果即可篡殷,有效提高了查詢速度。
六埋涧、總 結(jié)
SCF 框架作為 58 分布式架構(gòu)的基礎(chǔ)組件板辽,支撐了 58 集團(tuán)內(nèi)部萬(wàn)級(jí)別節(jié)點(diǎn)的網(wǎng)絡(luò)調(diào)用。本文主要介紹基本調(diào)用和監(jiān)控相關(guān)內(nèi)容棘催。還有很多負(fù)載均衡劲弦、網(wǎng)絡(luò)管理、故障節(jié)點(diǎn)剔除醇坝、服務(wù)鑒權(quán)邑跪、服務(wù)限流等模塊沒有展開。SCF 框架經(jīng)過(guò)多次的迭代,從最初的最簡(jiǎn)單的遠(yuǎn)程調(diào)用到現(xiàn)在服務(wù)治理周邊功能的完善画畅,后續(xù)也將不斷優(yōu)化砸琅,歡迎感興趣的同學(xué)一起溝通交流。