關於rpc的文章,網上的內容不少,但我仍是準備寫一下,主要是本身的理解吧。算是公衆號的第一篇技術類文章吧。php
上週給團隊小夥伴分享了也是下面基礎入門的內容,本文主要結合一個簡單框架yar的原理和源碼來講明詳細狀況。java
RPC的定義nginx
RPC,即 Remote Procedure Call(遠程過程調用),調用遠程計算機上的服務,就像調用本地服務同樣。算法
RPC能夠很好的解耦系統,如WebService就是一種基於Http協議的RPC。json
RPC的框架服務器
各類架構都是在必定環境背景下產生的,有跨語言的,java語言的,有php的,有基於http的,有基於http2的。以下:swoole
Thrift, gRPC, rpcx, motan, dubbox網絡
Phprpc, yar, swoole, Hprose 架構
RPC調用app
這裏以dubbo的架構圖爲例,dubbo主要分紅4個角色:消費者(Consumer),生產者(Provider),監控中心(Monitor),註冊中心(Registry)。
Provider: 暴露服務的服務提供方,生產者。
Consumer: 調用遠程服務的服務消費方。
Registry: 服務註冊與發現的註冊中心。
Monitor: 統計服務的調用次數和調用時間的監控中心。
調用方法以下:
0. 服務容器啓動,加載,運行服務提供者(server端)。
1.服務提供者在啓動時,向註冊中心註冊本身提供的服務。
2.服務消費者在啓動時,向註冊中心訂閱本身所需的服務。
3.註冊中心返回服務提供者地址列表給消費者,若是有變動,註冊中心將基於長鏈接推送變動數據給消費者。
4.服務消費者,從提供者地址列表中,根據指定的負載均衡算法,選一臺提供者進行調用,若是調用失敗,再選另外一臺調用。
5.服務消費者和提供者,在內存中累計調用次數和調用時間,定時每分鐘發送一次統計數據到監控中心。
以上是從網上其餘帖子上找的調用內容。基本上涵蓋了rpc框架的基本思路,monitor和registry不是必須的,若是有其餘方式替代, 也可省略或者換成其餘能夠實現功能的方式,如集中處理日誌。下面是咱們在實際使用中用到的內容。
業務背景:在DAU過億的某博,基礎數據獨立成平臺,這樣返回的PC,手機,H5實際上是基礎數據,要想被手機端所用,實際上須要加工而後傳到端上展現。而爲了統一化管理,網頁版和手機app版儘可能使用同一套解析。業務需求產生,在必定的歷史背景下,產生了rpc的調用模式。條條大路通羅馬,問題不止一個解決方案,咱們只是採用了一種,即結合http,nginx ,php-fpm等使用yar這個php擴展模塊實現這個業務。
嚴格意義上講yar不能獨立使用的,沒有dubbo,motan等那種本身獨立成框架體系。有些部分交給使用者本身實現,如日誌記錄,咱們的日誌記錄實際與yar關係不大。框架數據傳輸方式仍是藉助http,結合nginx,php-fpm等內容,基礎仍是個http的請求。(curl和sockert兩種方式,sockert目前咱們沒有使用)。沒有註冊發現服務的具體使用,這個徹底由lvs給咱們解決,服務的可用徹底交給了運維。
瞭解了各個角色,咱們來看下核心內容,Yar實現了rpc的基礎部分,即數據的譯碼/解碼和傳輸,我感受這兩部分是這個框架的核心。下面是流程圖:
請求過程:
Client須要遠程調用的時候,先初始化數據,數據主要包括三個部分header, packager_name,request_body,而後根據配置中的方式從pack_list選擇合適的序列化方式(msgpack,json,php)
對request_body進行序列化。 而後進行傳輸,傳輸方式 一樣是採用工廠方法,從已有的方式中選擇Curl/sockert方式進行傳輸數據,傳輸數據的過程實際就是發送一個http請求(咱們以curl爲例)。
服務器端監聽端口,底層網絡實現都利用現有的nginx, php-fpm。在PHP代碼中,實現的地方實例化一個類,而後根據request的內容,解析body,而後去初始化header, 得到packager_name,根據packer_name解析yar_body的內容。
如下是每次request和response的數據狀況。以request爲例傳輸的body數據包含三個部分,82byte的頭部,packager_name,和通過packager_name序列化的string。當這段數據傳到server端,按照固定的結構解析出來便可。
PACK過程是對數據做以下處理的過程。以msgpack爲例,對中間部分的數據進行序列化。生成最右邊的內容(序列化後的字符串有最右有差異,這裏只是拿json舉例)。
傳輸
yar目前有兩種傳輸方式,這個在以前提到過curl和socket方式。由於socket實際沒有用過因此這裏主要介紹curl。
curl的模塊主要以來底層的CURL模塊,主要封裝了以下方法。其中multi系列主要是解決並行請求的方案。
php_yar_curl_open
這個方法主要是用CURL建立一個鏈接對象。這裏有個複用的機制,當options &
YAR_PROTOCOL_PERSISTENT(0x1) 爲true的時候,會去已經使用的鏈接中找一下,若是存在而且沒有在用則複用。
php_yar_curl_send
這個函數主要是把request須要的數據,轉成字符串,而後放在請求的postfield裏面。
php_yar_curl_exec
將postfield內容,經過http請求發送出去。得到返回的內容。而後按照上述內容解析出結果。
原本還想分享grpc的protobuf和htt2,鑑於粗淺學習階段,網上的其餘博客文章更詳細,這裏就不寫了,等我實際有本身的理解再說啦。
凡事都沒有完美,也碰到過yar的不足之處,好比server的handle方法,解析post數據,執行對應的方法返回都在這裏了。沒有將請求的實際方法暴露,這樣在server端統計起來比較困難。曾試圖在源碼中加個getCallMethod方法去標記這個內容,不過只是本身玩玩,沒有實際應用...
有同窗問我爲啥咱們當時要用yar,也許別的框架也能知足咱們的需求,必定的歷史背景決定必定的技術架構。技術的選型更多傾向於開發者的水平----即選擇本身駕馭的技術,站在巨人的肩膀上展望遠處。