RPC全程遠程方法調用,已經在各大小公司被普遍使用,種類也是不少好比:Dubbo,Spring cloud那一套,GRPC,Thrift,可能還有不少公司自研的等等;每一個公司均可能根據本身的業務需求,場景選擇本身合適的RPC框架;但大致的考察維度無非就這麼幾個:性能,可擴展性,跨平臺,功能性,可監控,使用性;因此咱們若是要設計一個RPC框架,能夠從這幾個角度去考慮。面試
做爲微服務中的核心組件,在一個系統中RPC的調用量每每是很高的,因此性能是一個很重要的考慮點;既然是遠程調用,必然牽扯到網絡鏈接,而I/O模型的選擇直接影響到性能,網絡的長鏈接短鏈接,序列化方式也都影響性能;算法
常見的Unix5種I/O模型分別是:阻塞I/O,非阻塞I/O,I/O複用(select,poll,epoll等支持I/O多路複用),信號驅動I/O,異步I/O;從早期的阻塞I/O方式只能建立大量的線程來保證每一個用戶互不影響,到如今普遍使用的I/O多路複用模型,再到異步I/O;從select模型到如今主流的epoll模型,性能有了質的升級;固然咱們不必本身去實現,能夠直接使用網絡通信框架Netty,Mina等;數據庫
短鏈接表示每次通信完就關閉鏈接,而長鏈接通信完繼續保持鏈接,這樣下次再通信就不須要從新創建鏈接了,若是通信頻繁,很明顯長鏈接性能更高;可是長鏈接須要作一些額外的工做,好比保活處理;另外就是若是客戶端太多的話,服務器端是沒法支撐的。編程
網絡傳輸中的數據都須要通過序列化和反序列化處理,因此這一塊的性能也很重要;常見的序列化包括:json和二進制方式,json常見的如fastjson,jackson等,二進制如protobuf,thrift ,kryo等;這個能夠分別從序列化的性能,大小,以及使用方便性考慮;固然穩定性和安全性也須要考慮,好比fastjson頻繁爆出安全漏洞;json
這裏主要講的是應用層協議,RPC通常都會自定義協議,固然也有直接使用現有協議的好比http協議;自定義協議能夠本身掌控,協議能夠作的很小很精簡,固然解碼和編碼須要本身去實現;若是使用現有的http協議,相對來講整個協議包是比較大的,可是已是一種規範了,不少東西能夠直接拿來用,更加通用;緩存
可擴展性能夠從兩個角度來看,一個是服務提供方和消費方的負載均衡策略;另外一個就是用戶能夠對框架進行自定義擴展;安全
RPC的兩個核心組件服務提供方和消費方,須要提供橫向擴展的機制,用以達到更高的負責,好比Spring Cloud、Dubbo提供的註冊中心,而後再結合容錯機制達到負載均衡的效果,很容易達到橫向擴展;服務器
一個好的框架是支持用戶自定義的,用戶根據本身的需求實現自定義擴展;JDK提供了SPI機制,很常見的一個場景就是數據庫驅動;另外Dubbo在此基礎上提供了更增強大的SPI機制;這種擴展對RPC來講是多方面的,能夠是底層的通信框架擴展,序列化擴展,註冊中心擴展,容錯機制擴展,協議擴展等等;微信
如今開發語言多種多樣,若是能作成跨平臺,固然是一大優點,可是可能每每爲了跨平臺,會在一些地方作權衡讓步,固然難度也更大;因此咱們在實現一個RPC時須要明確本身的定位,就是針對某一種語言的仍是跨平臺支持;常見的支持跨平臺的RPC框架如GRPC,Thrift;
實現機制能夠參考一下,都有本身的一套接口定義語言IDL而後經過不一樣的代碼生成器,生成各類編程語言消費端和服務器端代碼,來保證不一樣語言直接的相關通訊。網絡
做爲一個RPC框架,除了最核心的通訊模塊,序列化模塊以外,功能性模塊也很重要,每每爲開發者節省了大量的時間,開發者可能由於RPC的某個功能而選擇用此框架;常見的功能包括:容錯機制,負載均衡機制,同步異步調用,結果緩存,路由規則,服務降級,多版本,線程模型等;
在錯綜複雜的網絡環境中,遠程調用失敗再正常不過了,容錯機制就顯得很是有必要了,常見的容錯機制好比:失敗重試,快速失敗,失敗直接忽略,並行調用多個服務器只要一個成功即返回等;用戶能夠根據需求選擇本身合適的容錯方案;
上面提到服務提供方和消費方的可擴展性,消費方面對多個提供方的時候須要有必定的負載均衡策略,來保證系統的穩定性;常見的策略如:隨機,輪詢,最少活躍調用數,一致性hash等;
常見的同步調用有些場景沒法知足需求,好比同時須要調用多個遠程方法,而這其中可能有些執行比較慢的;這時候異步調用就顯得重要了,能夠同時異步發送多個請求,等待時間就是響應最慢的請求;具體能夠經過Future,CompletableFuture來實現;
緩存一直是性能的不二法寶,某些場景下可能對服務提供方響應的數據實時要求性並不高,這時候若是能夠在服務提供端提供結果的緩存機制,那麼在性能上是一個很大的提高;能夠本身設計一個本地緩存,固然也能夠直接整合第三方緩存框架好比ehcache,jcache等;
服務提供方每每是不少的,用戶可能有一些特殊的需求,能夠按照本身定義個規則來作路由,好比咱們常常作的灰度發佈,結合RPC提供的路由規則來實現會很簡單;此規則多是條件表達式,腳本,標籤等,咱們設計的RPC框架須要有一個給用戶定義規則的地方,好比經過註冊中心來實時推送,另外還須要相關的引擎來處理規則,好比腳本引擎;
系統的高可用性原則中,很重要的一條就是降級處理,在一些非核心的功能中,能夠在出現超時/故障時或者直接設置爲降級服務,給一個統一的響應,這樣能夠把寶貴的資源留給那些核心的功能;能夠參考Dubbo的實現,向註冊中心寫入動態配置覆蓋規則,從而實現實時降級處理;
接口升級時常有的事情,咱們可能會爲了兼容以前的版本絞盡腦汁,但有時候仍是無能爲力,這時候多版本就顯得很重要了,可讓兩個版本同時存在,等到合適的機會在慢慢升級;像dubbo這種服務的維度就是服務名+版本號,因此很好實現多版本;而Spring Cloud維度沒有到版本號,能夠經過路由規則去實現;
在系統的高可用原則中,線程隔離是一條重要的原則,爲何要作隔離,能夠拿Dubbo及其底層通訊框架netty爲例,netty做爲通訊框架自己是有本身的線程模型,若是業務處理線程直接使用底層的通訊線程模型,這樣就會出現由於業務阻塞而致使通訊線程模型阻塞;這時Dubbo提供本身的線程模型就尤其重要,能夠作到線程隔離,業務線程不影響通訊線程;
固然做爲一個RPC框架實現的功能能夠不少,這裏主要講一些咱們日常用的比較多的功能;
一個運行穩定的系統,沒有一個專門的監控平臺是不行的,固然RPC也不例外,常見的好比dubbo的monitor模塊,Spring Cloud Admin等;對於一個RPC咱們主要監控:服務提供者有哪些,服務消費者有哪些,以及它們的狀態,最好還有一些統計功能好比一段時間內的調用量,成功率,失敗率,平均響應時間,最大響應時間,最大併發量等等;
最後說一下,咱們設計出來的框架最終仍是要給用戶使用的,因此用戶是否能夠方便的使用也是一個很重要的點,某些框架可能就是由於使用繁瑣致使最終被棄用;好比註解的方式相比較xml的方式就簡單很多;還有好比服務維度來講:dubbo維度是接口,而Spring cloud維度是應用,總體來看Spring cloud使用起來更加方便;固然簡單的API,文檔以及Demo對開發者來講也是必不可少的;
RPC本質上其實就是一次網絡調用,不少設計其實都是在圍繞,如何把它變成一個高可用,高併發的框架;其實這些設計理論適用於大部分的系統,都在爲達到此目標而努力。
能夠關注微信公衆號「 回滾吧代碼」,第一時間閱讀,文章持續更新;專一Java源碼、架構、算法和麪試。