迅速的說清楚rpc原理

     

第一個問題是服務提供者進程若是被kill -9暴力殺死,不能主動調用srem命令怎麼辦?html

這個時候服務列表中多了一個黑地址指向了不存在的服務而消費者徹底不知道,這個時候服務中介就成了黑中介了。那該怎麼辦呢?java

咱們引入服務保活和檢查機制,並更換數據結構。服務提供者須要每隔5秒左右向服務中介彙報存活,服務中介將服務地址和彙報時間記錄在zset數據結構的value和score中。服務中介須要每隔10秒左右檢查zset數據結構,踢掉彙報時間嚴重落後的服務地址項。這樣就能夠準實時地保證服務列表中服務地址的有效性。git

第二個問題是服務列表變更時如何通知消費者。有兩種解決方案。github

第一種是輪詢,消費者須要每隔幾秒查詢服務列表是否有改變。若是服務不少,服務列表很大,消費者不少,redis會有必定壓力。因此這時候能夠引入服務列表的版本號機制,給每一個服務提供一個key/value設置服務的版本號,就是在服務列表發生變更時,遞增這個版本號。消費者只須要輪詢這個版本號的變更便可知道服務列表是否發生了變化。由於服務列表比較穩定,僅在網絡嚴重抖動的狀況下才會頻繁發生變更,因此redis幾乎沒有壓力。redis

第二種是採用pubsub。這種方式及時性要明顯好於輪詢。缺點是每一個pubsub都會佔用消費者一個線程和一個額外的redis鏈接。爲了減小對線程和鏈接的浪費,咱們使用單個pubsub廣播全局版本號的變更。所謂全局版本號就是任意服務列表發生了變更,這個版本號都會遞增。接收到版本變更的消費者再去檢查各自的依賴服務列表的版本號是否發生了變更。這種全局版本號也能夠用於第一種輪詢方案。網絡

 

  • 1)服務消費方(client)調用以本地調用方式調用服務;
  • 2)client stub接收到調用後負責將方法、參數等組裝成可以進行網絡傳輸的消息體;
  • 3)client stub找到服務地址,並將消息發送到服務端;
  • 4)server stub收到消息後進行解碼;
  • 5)server stub根據解碼結果調用本地的服務;
  • 6)本地服務執行並將結果返回給server stub;
  • 7)server stub將返回結果打包成消息併發送至消費方;
  • 8)client stub接收到消息,並進行解碼;
  • 9)服務消費方獲得最終結果。

RPC的目標就是要2~8這些步驟都封裝起來,讓用戶對這些細節透明。數據結構

 

怎麼封裝通訊細節才能讓用戶像以本地調用方式調用遠程服務呢?對java來講就是使用代理!java代理有兩種方式:1) jdk 動態代理;2)字節碼生成。儘管字節碼生成方式實現的代理更爲強大和高效,但代碼不易維護,大部分公司實現RPC框架時仍是選擇動態代理方式。併發

 

1)client線程每次經過socket調用一次遠程接口前,生成一個惟一的ID,即requestID(requestID必需保證在一個Socket鏈接裏面是惟一的),通常經常使用AtomicLong從0開始累計數字生成惟一ID;框架

2)將處理結果的回調對象callback,存放到全局ConcurrentHashMap裏面put(requestID, callback);socket

3)當線程調用channel.writeAndFlush()發送消息後,緊接着執行callback的get()方法試圖獲取遠程返回的結果。在get()內部,則使用synchronized獲取回調對象callback的鎖,再先檢測是否已經獲取到結果,若是沒有,而後調用callback的wait()方法,釋放callback上的鎖,讓當前線程處於等待狀態。

4)服務端接收到請求並處理後,將response結果(此結果中包含了前面的requestID)發送給客戶端,客戶端socket鏈接上專門監聽消息的線程收到消息,分析結果,取到requestID,再從前面的ConcurrentHashMap裏面get(requestID),從而找到callback對象,再用synchronized獲取callback上的鎖,將方法調用結果設置到callback對象裏,再調用callback.notifyAll()喚醒前面處於等待狀態的線程。

 

zookeeper就是個分佈式文件系統,每當一個服務提供者部署後都要將本身的服務註冊到zookeeper的某一路徑上: /{service}/{version}/{ip:port}, 好比咱們的HelloWorldService部署到兩臺機器,那麼zookeeper上就會建立兩條目錄:分別爲/HelloWorldService/1.0.0/100.19.20.01:16888  /HelloWorldService/1.0.0/100.19.20.02:16888。

zookeeper提供了「心跳檢測」功能,它會定時向各個服務提供者發送一個請求(實際上創建的是一個 socket 長鏈接),若是長期沒有響應,服務中心就認爲該服務提供者已經「掛了」,並將其剔除,好比100.19.20.02這臺機器若是宕機了,那麼zookeeper上的路徑就會只剩/HelloWorldService/1.0.0/100.19.20.01:16888。

服務消費者會去監聽相應路徑(/HelloWorldService/1.0.0),一旦路徑上的數據有任務變化(增長或減小),zookeeper都會通知服務消費方服務提供者地址列表已經發生改變,從而進行更新。

 

 

1.3  通訊

消息數據結構被序列化爲二進制串後,下一步就要進行網絡通訊了。目前有兩種IO通訊模型:1)BIO;2)NIO。通常RPC框架須要支持這兩種IO模型,原理可參考:《一個故事講清楚 NIO》

如何實現RPC的IO通訊框架?1)使用java nio方式自研,這種方式較爲複雜,並且頗有可能出現隱藏bug,見過一些互聯網公司使用這種方式;2)基於mina,mina在早幾年比較火熱,不過這些年版本更新緩慢;3)基於netty,如今不少RPC框架都直接基於netty這一IO通訊框架,好比阿里巴巴的HSF、dubbo,Twitter的finagle等。

 

https://www.cnblogs.com/panxuejun/p/6094790.html

1.3  通訊

消息數據結構被序列化爲二進制串後,下一步就要進行網絡通訊了。目前有兩種IO通訊模型:1)BIO;2)NIO。通常RPC框架須要支持這兩種IO模型,原理可參考:《一個故事講清楚 NIO》

如何實現RPC的IO通訊框架?1)使用java nio方式自研,這種方式較爲複雜,並且頗有可能出現隱藏bug,見過一些互聯網公司使用這種方式;2)基於mina,mina在早幾年比較火熱,不過這些年版本更新緩慢;3)基於netty,如今不少RPC框架都直接基於netty這一IO通訊框架,好比阿里巴巴的HSF、dubbo,Twitter的finagle等。

相關文章
相關標籤/搜索