RPC
遠程程序呼叫(Remote Procedure Call,RPC)框架作為架構微服務化的基礎組件,能大大降低架構微服務化的成本,提高服務呼叫方與服務提供方的開發效率,屏蔽跨行程呼叫函式(服務)的各類復雜細節,其呼叫原理如圖6-13所示,讓服務提供方像實作本地函式一樣來實作分布式服務,開發人員不用考慮底層通信協議;讓服務呼叫方像呼叫本地函式一樣呼叫遠端函式,多數RPC框架以面向介面的方式提供遠程方法的呼叫,對開發人員非常友好,
客戶端存根(client stub)用于存放服務器端地址資訊,將客戶端的請求引數資料資訊打包成網路訊息,再通過網路傳輸發送給服務器端,服務器端存根接收客戶端發送過來的請求訊息并進行解包,再呼叫本地服務進行處理,
RPC的原理與我們平時打電話的程序非常相似,服務消費者叫作客戶端,服務提供者叫作服務器端,兩者通常位于網路上兩個不同的地址,要完成一次RPC呼叫,就必須先建立網路連接,建立連接后,雙方必須按照某種約定的協議進行網路通信,這個協議就是通信協議,雙方能夠正常通信后,服務器端接收到請求時,需要以某種方式進行處理,處理成功后,把請求結果回傳給客戶端,為了減少傳輸的資料大小,要對資料進行壓縮,也就是對資料進行序列化,為了實作遠程服務的呼叫,RPC框架要做到如下最基本的4件事,

▲圖6-13 RPC框架呼叫原理
1.客戶端如何找到服務器端地址
要完成一次服務呼叫,首先要解決的問題是服務呼叫方如何得到服務提供方的地址,其中注冊中心扮演了關鍵角色,服務提供方把自己所提供的服務串列以及當前節點地址登記到注冊中心,服務呼叫方就可以查詢注冊中心以得到服務提供方的地址,為了實作去中心化設計,多數RPC產品采用本地負載均衡策略,即呼叫方啟動時從注冊中心拉取服務地址資訊后,在本地快取,運行程序中真正發起呼叫時,直接從本地快取讀取服務地址資訊,根據一定的負載均衡演算法選取某一個地址,發起點對點的直接呼叫,這樣就避免了中心化的服務總線進行服務路由,運行效率更高,彈性伸縮更方便,當服務器端地址資訊發生變更時(如節點上下線),由注冊中心實時推送變更資訊到所有的呼叫方同步更新,
2.服務器端如何處理請求
當使用基于RPC的行程間通信時,客戶端向服務器端發起請求,服務器端處理該請求并發回回應,有些客戶端可能處在阻塞狀態直到得到回應,而有些客戶端可能會有一個回應式的非阻塞架構,與使用訊息機制的完全異步化架構不同,RPC客戶端都假定回應會及時到達,
在遠程呼叫中,客戶端和服務器端已經建立了網路連接,服務器端又該如何處理客戶端的請求呢?通常來講,服務器端I/O的方式通常分為3種處理方式:同步阻塞(Blocking I/O,BIO)方式、同步非阻塞(Non-blocking I/O,NIO)方式、異步非阻塞(Asynchronous I/O,AIO)方式,
(1)同步阻塞方式,
客戶端每發一次請求,服務器端就生成一個執行緒去處理,當客戶端同時發起很多請求時,服務器端需要創建很多執行緒去處理每一個請求,如果達到了系統最大的執行緒數,新的請求就沒法處理了,
(2)同步非阻塞方式,
NIO本身是基于事件驅動思想來完成的,其主要解決的是BIO的高并發問題:在使用同步I/O的網路應用中,如果要同時處理多個客戶端請求或是在客戶端同時和多個服務器進行通信,就必須使用多執行緒來處理,也就是說,將每一個客戶端請求分配給一個執行緒來單獨處理,這樣做雖然可以達到我們的要求,但同時又會帶來另一個問題,由于每創建一個執行緒,就要為這個執行緒分配一定的記憶體空間(也叫作業存盤器),而且作業系統本身對執行緒的總數有一定的限制,如果客戶端的請求過多,服務器端程式可能會因為不堪重負而拒絕客戶端的請求,甚至服務器可能因此而癱瘓,
NIO基于reactor,當socket有流可讀或可寫入socket時,作業系統會相應地通知應用程式進行處理,應用程式再將流讀取到緩沖區或寫入作業系統,也就是說,這時已經不是一個連接就要對應一個處理執行緒了,而是有效的請求對應一個執行緒,當連接沒有資料時,是沒有作業執行緒來處理的,BIO與NIO的一個比較重要的不同之處在于,我們使用BIO時往往會引入多執行緒,每個連接對應一個單獨的執行緒,而NIO使用單執行緒或者只使用少量的多執行緒,多個連接共用一個執行緒,
(3)異步非阻塞方式,
與NIO不同,當進行讀寫操作時,AIO只需直接呼叫API的read或write方法,這兩種方法均為異步的,對讀操作而言,當有流可讀取時,作業系統會將可讀的流傳入read方法的緩沖區,并通知應用程式;對寫操作而言,當作業系統將write方法傳遞的流寫入完畢時,作業系統主動通知應用程式,可以理解為,read/write方法都是異步的,完成后會主動呼叫回呼函式,
客戶端只需要發起一個 I/O 操作后立即回傳,等 I/O 操作真正完成以后,客戶端會得到 I/O 操作完成的通知,此時客戶端只需要對資料進行處理就可以了,而不需要進行實際的 I/O 讀寫操作,因為真正的 I/O 讀取或者寫入操作已經由內核完成了,這種方式的優勢是客戶端無須等待,不存在阻塞等待問題,
3.如何進行序列化和反序列化
客戶端和服務器端互動時將引數或結果轉化為位元組流在網路中傳輸,那么將資料轉化為位元組流或者將位元組流轉換成能讀取的固定格式時,就需要進行序列化(資料編碼)和反序列化(資料解碼),序列化和反序列化的速度也會影響遠程呼叫的效率,
常用的序列化方式分為兩類:文本類(如XML、json等)、二進制類(如Hessian、Thrift等),而具體采用哪種序列化方式,主要取決于3個方面的因素,
(1)支持資料結構種類的豐富度,資料結構種類支持得越多越好,這樣對使用者來說在編程時更加友好,有些序列化框架如Hessian 2.0還支持復雜的資料結構(比如Map、List等),
(2)跨語言支持,序列化方式是否支持跨語言也是一個很重要的因素,否則使用的場景就比較局限,比如Java序列化只支持Java語言,所以不能用于跨語言的服務呼叫,
(3)性能,主要看兩點,一是序列化后的壓縮比,二是序列化的速度,以常用的 protobuf序列化和json序列化協議為例來對比分析,protobuf序列化的壓縮比和速度都要比 json 序列化高很多,所以對性能和存盤空間要求比較高的系統選用protobuf序列化更合適;而 json 序列化雖然性能要差一些,但可讀性更好,更適合對外部提供服務,
4.如何進行網路傳輸(選擇何種網路協議)
多數RPC框架會選擇TCP作為傳輸協議,也有部分會選擇HTTP(如gRPC使用HTTP/2),不同的協議各有利弊,TCP更加高效,而HTTP在實際應用中更加靈活,
RESTful
REST全稱是Representational State Transfer,中文意思是表述性狀態轉移,它首次出現在2000年Roy Fielding的博士論文中,Roy Fielding是HTTP規范的主要撰寫者之一,他在論文中提到:“我寫作這篇文章的目的,就是想在符合架構原理的前提下,理解和評估以網路為基礎的應用軟體的架構設計,得到一個功能強、性能好、適宜通信的架構,REST指的是一組架構約束條件和原則,”如果一個架構符合REST的約束條件和原則,我們就稱它為REST風格的(RESTful)架構,
REST提供了一系列架構約束,當作為整體使用時,它強調組件互動的可擴展性、介面的通用性、組件的獨立部署,以及那些能減少互動延遲的中間件,它強化了安全性,也能封裝遺留系統,
——Roy Fielding
REST中一個關鍵概念是“資源”,它把一切程式能夠訪問到的業務物件或者處理程序統一定義為資源,REST使用HTTP動詞來操作這些資源,并采用特定的語意規范,使用URL參考這些資源,例如,GET請求回傳資源的表示形式,該資源通常采用XML或者json的格式表示,但也可以使用其他格式(如二進制),POST請求創建新資源,PUT更新資源,DELETE洗掉資源,
RESTful是一種網路應用程式API的設計風格和開發方式,基于HTTP,可以使用XML格式定義或json格式定義,最常用的資料格式是json,由于json能直接被JavaScript讀取,因此以json格式撰寫的REST風格的API具有簡單、易讀、易用的特點,如圖6-14所示,相比于RPC協議,HTTP更規范、更標準、更通用,無論哪種語言都支持HTTP,如果你想對外開放API,開放平臺外部的編程語言多種多樣,你無法拒絕對每種語言的支持,現在開源中間件,基本最先支持的幾個協議都包含HTTP RESTful,

▲圖6-14 REST風格的服務提供方
前后端分離架構、微服務架構都有一個共同的愿景:使不同的團隊之間實作松耦合,各自能獨立地開發和部署,這背后需要依靠一套設計良好的API,它必須更加輕量、可靠、跨平臺,因此RESTful API脫穎而出,系統應用提供RESTful API有什么好處呢?由于API就是把Web App的功能全部封裝,因此通過API操作資料可以把前端和后端的代碼隔離,使得后端代碼易于測驗,前端代碼撰寫更簡單,REST風格協議的特點如下,
(1)每個統一資源識別符號(Uniform Resource Identifier,URI)代表一種資源,任何事物只要有被參考的必要,它就是一個資源;要讓一個資源可以被識別,需要有一個唯一標識,在Web中這個唯一標識就是URI,
(2)客戶端使用GET、POST、PUT、DELETE這4個表示操作方式的動詞對服務器端資源進行操作:GET用來獲取資源,POST用來新建資源(也可以用于更新資源),PUT用來更新資源,DELETE用來洗掉資源,
(3)通過操作資源的表述形式來操作資源,資源在外部的具體呈現可以有多種表述形式(例如文本資源可以采用HTML、XML、json等格式,圖片可以使用PNG或JPG格式展現出來),在客戶端和服務器端之間傳送的也是資源的表述,而不是資源本身,
(4)客戶端與服務器端之間的互動在請求之間是無狀態的,從客戶端到服務器端的每個請求都必須包含理解請求所必需的資訊,這里說的無狀態通信原則,并不是說客戶端應用不能有狀態,而是指服務器端不應該保存客戶端狀態,
很多開發人員表示他們的代碼是基于HTTP的API進行開發的,那么用了HTTP就叫REST風格嗎?答案是否定的,Leonard Richardson曾提出過一個RESTful成熟度模型,模型中從level 0到level 3,數字越大,表示采用RESTful的成熟度越高,如圖6-15所示,成熟度模型并不是什么工業標準,這里僅作為參考來講解RESTful思想,讀者可以對比思考自己專案中是如何使用RESTful API的,

▲圖6-15 Leonard Richardson提出的RESTful成熟度模型
在此簡單說明RESTful成熟度模型的4個等級,
level 0:沒有明確的資源概念,僅作為通道,只有一個URL,只使用單個HTTP方法,這個階段還稱不上使用了RESTful,HTTP僅作為RPC的傳輸通道,想象一下,在本地呼叫某個方法時,它可能需要一個輸入物件,處理完成后回傳另一個結果物件,這個階段的HTTP只是用在請求呼叫遠程方法時,傳遞輸入物件給服務器端,并在方法執行結束后,傳遞結果物件給客戶端,
level 1:有明確的資源概念,存在很多URL,只使用單個HTTP方法,客戶端每次請求都是對服務器端某個或某一類資源的操作,服務器端一切可被標識的事物都可以稱為資源,例如,一張圖片、一個訂單、一個產品、一個流程、最活躍的10個用戶等,每個資源都可以用URI來表示,所以從現在開始,URI用來標識一個資源,
level 2:有明確的資源操作,有很多URL,使用HTTP作為操作資源的統一介面,通常將對于資源的CRUD式操作分別映射到4個HTTP方法(有時也會使用新增的PATCH方法),這里的每一個動詞都有它自身的語意,
level 3:這一階段的RESTful API具備了自描述的能力,從而實作服務自動發現,自描述是指告訴用戶當前狀態以及下一步可能的各種操作,如果將應用想象成一個大的狀態引擎,那么我們每次針對URI的操作,都是將應用從一種狀態轉變為另一種狀態,而當前狀態(可表述的)里又包含了下一步可進行的操作,一步一步地傳輸狀態,直至完成所有的操作,一般達到level 3成熟度模型的應用,使用超媒體作為應用狀態的引擎(Hypermedia as the Engine of Application State,HATEOAS),
REST風格具有開放、標準、通用的特點,尤其在跨語言的異構環境下系統的互聯互通具有天然的優勢,對于REST風格的應用開發模式,有兩點值得注意,
(1)RESTful API并不是十全十美的,由于HTTP是應用層協議,本身比TCP慢一些;加之資料本身是自描述的文本格式,需要占用更多的帶寬,因此相比于RPC,RESTful API的速度會稍慢一些,但是,速度可以通過技術手段彌補,例如HTTP/2、CDN、七層負載均衡等,在某些對速度要求不嚴苛的場景下,RESTful API帶來的靈活性和伸縮性更具有價值,
(2)并不是所有業務場景都適合采用RESTful API,也不是在設計API時就一定要遵守所有規則,如何取舍還要看具體業務需求,適合的才是最好的,畢竟架構也是伴隨業務的發展而不斷演進的,
優缺點對比
RPC和RESTful是目前兩種主流的微服務之間的通信協議風格,兩者各有利弊,分別適用于不同的場景,表6-2是這兩種風格的主要技術指標對比,
表6-2 RPC與RESTful兩種協議風格對比

RPC主要是基于TCP/IP的,而RESTful服務主要是基于HTTP的,HTTP是在傳輸層協議(TCP)之上的,由于RPC普遍采用二進制壓縮的序列化引數,資料傳輸量方面會比REST風格的json(或者XML)小很多,所以總的來說,從運行效率來看,RPC當然是要更勝一籌,RESTful的優勢主要體現在通用、開放、標準方面,兩者的優缺點詳細說明如下,以方便讀者在實際應用專案中進行綜合分析選擇,
(1)RPC的優點,
- 對于服務器端的開發人員而言,容易設計、開發,
- 對于消費者而言,呼叫非常簡單,
- 便于做集中的監控,
- 基于socket的二進制RPC協議,建立連接延遲低、網路傳輸效率高,
- 支持有狀態的長連接,可進行雙向通信,實時性好,
- 在各個企業的使用較為成熟,許多企業都有自己的RPC實踐,并已廣泛應用在生產環節,
(2)RPC的缺點,
- 緊耦合:API一旦發布,就難以再做改動,客戶端必須使用特定的框架,而且需要引入API包,
- 沒有統一的設計風格:增加了客戶端開發人員的學習成本,難以實作通用的客戶端庫,每個RPC框架都有各自的協議,通常以動詞的形式設計API,一個功能就增加一個API,設計時很少考慮領域模型,
- 掩蓋網路的復雜性:開發人員很容易混淆遠程呼叫與本地呼叫,實際上網路呼叫與本地呼叫是完全不同的,RPC的呼叫方式,讓使用者很難意識到是在進行網路呼叫,忽略了針對網路復雜性的處理,這樣會損害用戶(客戶端)可感知的性能,
(3)RESTful API的優點,
- 使用HTTP作為應用層協議(注意:不是傳輸層),沒有耦合性,
- 可以使用瀏覽器擴展(如Postman)或者curl之類的命令列來測驗RESTful API,
- 客戶端使用任意支持HTTP的工具即可,使用類似Netflix Feign這樣的專門設計的工具,可以做到接近RPC的呼叫方式,
- 可以方便地發布到外網環境,
- HTTP對防火墻友好,可以設定各種安全策略,
- 基于資源的設計思想,強迫設計人員抽象資源,思考模型,使設計人員加深對業務模型的理解,
- 不需要中間代理,簡化了系統架構,
(4)RESTful API的缺點,
傳統的觀念認為RESTful API不具備RPC的很多特性,不能作為企業級應用廣泛使用的API管理方式,實際上只要能夠正確實施,RESTful API也可以具備RPC 框架的許多特性,
- 誤解1:RESTful API 不便于集中管理,
基于“服務發現”和“API網關”,RESTful API服務也可以實作統一的服務注冊、服務發現,“API網關”可作為服務的統一出口,反向代理全部的底層服務,實作統一的安全控制和權限管理,
- 誤解2:RESTful API 不便于客戶端呼叫,
RESTful API直接使用HTTP作為應用層協議,實際上只要支持HTTP的任何工具都可以完成對RESTful API的呼叫,Web層也可以使用原生JavaScript或jQuery呼叫,
不可否認,傳統的HTTP操作庫一般只是支持HTTP隧道模式的呼叫方式,即把HTTP作為傳輸協議,針對媒體型別轉換也沒有特殊處理,只回傳資料流或者文本資料,對資源的概念也沒有特別設計,如Apache Http Components、jQuery等,
但是目前已經有很多專門針對RESTful API撰寫的客戶端呼叫工具,如Java的Netflix Feign、Sping RestTemplate,Feign使用基于注解的形式定義客戶端介面,框架會自動生成本地代理類,直接使用類似于本地方法呼叫的形式呼叫,Spring Cloud專案下的Spring Cloud Netflix Feign子專案結合了Spring Boot和Netflix Feign做了封裝,更便于使用,
再者在前端領域,現在成熟的前端框架都提供了Resource插件,專門用于RESTful 介面的操作,如Vue下的vue-resource,AngularJS的ng-resource等,針對RESTful API的呼叫以及資源導航都做了很優秀的設計,
書籍推薦
企業級云原生架構 技術、服務與實踐

1.基于作者在阿里公司多年的大型專案架構設計實踐經驗,介紹云原生相關技術及產品
2.內容深入淺出,既有方法論詳述也有技術原理深入分析
3.理論與實踐并重,深入闡述云原生架構設計
4.緊貼技術趨勢,把握主流技術發展
《企業級云原生架構:技術、服務與實踐》較為全面、系統地介紹了云原生架構相關的方法論與技術產品,并結合作者多年的大型專案建設實施經驗,闡述了分布式環境下面向云原生的架構設計最佳實踐,本書主要分為4個部分,分別是云原生概述、云原生技術、云原生服務、云原生架構實踐,本書兼顧理論、技術與實踐,對從事相關行業的讀者具有很好的學習指導意義,
《企業級云原生架構:技術、服務與實踐》面向的讀者物件為互聯網行業的業務咨詢師、系統架構師,以及相關領域的技術開發人員,
作者簡介
劉景應,具有20年軟體開發、架構設計以及解決方案咨詢經驗,目前就職于阿里云云原生應用平臺,熟悉互聯網企業的技術堆疊與開發管理模式,對云原生相關技術、產品、架構有較為全面的理解,是國內云原生技術的先行者和布道者,致力于推動云原生相關理念和技術在國內IT應用中的落地實踐;具備豐富的大型實時在線應用系統的架構設計經驗,曾負責了多個部委以及行業頭部客戶的核心業務系統的架構咨詢與技術指導,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/296542.html
標籤:其他
上一篇:高并發系統設計二(架構分層)
