RPC:Remote Procedure Call(遠程服務調用)
RPC是做什么的
通過RPC框架機器A某個進程可以通過網絡調用機器B上的進程方法,就像在本地上調用一樣。
RPC可以基於HTTP或者TCP協議通信,TCP協議相對性能較高。
調用圖示(引用zhanglijun童鞋的圖)

圖片描述:
- client發出調用方法(服務)的請求
- client stub作為中轉站,進行
請求接口、方法、參數以及服務地址、請求Id的封裝,包裝成RequestMessage對象、序列化——編碼,最后傳輸到網絡上去 - 服務端(service)經過網絡接受到RequestMessage
- 首先server stub進行字節流的反序列化、解析
requestMessage——解碼,最后將請求轉發給server - server調用
method(...),返回結果給server stub - server stub將
result、返回狀態碼、請求id等進行包裝成ResponseMessage、序列化——編碼、傳輸 - client stub得到字節流,進行反序列化、解析
ResponseMessage——解碼,將結果返給client - client最終得到結果
RPC框架就是將2-7這幾個步驟封裝起來,幫忙處理網絡IO、編解碼、序列化、服務尋址等工作,這些工作對於調用方和服務方都是透明的。由此,我們也可以得出結論,我們就需要使用代碼實現2-7步驟所需要處理的工作。下面列舉實現一個輕量級RPC框架所需要解決的幾個問題。
RPC框架的實現需要解決的幾個問題
如果想實現一個輕量級rpc框架需要解決什么問題呢:
請求封裝問題
機器A想要調用機器B上面的方法method(),那么A機器就需要詳細描述自己的需求——要調用那個ip上那個端口的進程、調用哪個service,這些都需要描述清楚。
詳細如下:
- 接口名稱,一個接口提供一種服務,比如
HelloworldService - 方法名稱,定位一個接口內的某個方法
- 參數——類型與值,client調用就需要client指定入參,服務端是肯定不知道這些的
- 超時時間
- requestId,每一個client發出請求時都需要綁定一個唯一指定requestId,這是因為在並發請求的時候一個服務方可能服務多個client的請求,有了這個requestId,client stub才能將返回結果發放給特定的請求線程而不會混淆。
返回消息對象封裝問題
- 返回值
- 狀態code,表示這次請求成功與否
- requestid
序列化的問題
網絡IO需要傳輸請求消息對象以及響應消息對象,這樣才能達到雙方通信的目的。但是網絡是以字節(byte)准確的說是比特傳輸信息的,因此需要一種技術將對象轉換成字節(序列化),以及再將字節轉換成對象(反序列化)。java自身是有序列化工具的,借助ObjectOutputStream等類就可以序列化對象。但是他的性能、拓展性都不是很好,因此一般借助於第三方的序列化工具
目前,互聯網公司主流使用protobuf、thrift、jackson、hessian等
網絡通信問題
消息序列化之后就是網絡通信了,使用java nio是可以實現一個通信框架的,但這屬於高難度動作——專家模式。就碼農way而言,使用netty通信框架,省事省力,經過測試完善的框架也不容易出錯
服務發布的問題
一般情況,如果客戶端依賴的服務掛掉或者切換地址等,那么客戶端可能就無法工作或者需要改變配置。為了能夠達到自動切換服務、尋址等問題,就需要一個服務注冊和發現功能的框架。市面上流行的是zookeeper,他可以將上線的服務注冊進去,提供給客戶端調用。zookeeper會不斷對服務進行心跳檢測,一旦服務掛掉,就會在注冊中心把他消掉。客戶端可以在注冊中心切換到其他可用的服務
zookeeper的服務注冊格式:/{service}/{version}/{ip:port}
關於RPC框架的初體驗
這里並不是商用的RPC框架,沒有服務治理、插件支持等功能,使用的是@黃勇寫的輕量級分布式 RPC 框架,目的在於理解rpc框架核心代碼的功能模塊結構、相關技術選型以及如何運行等幾個問題
關於框架的幾點理解
rpc-sample-api模塊,定義了服務接口,這個代碼無論客戶端和服務端都需要用到的rpc-sample-server模塊,對rpc-sample-api的接口進行了實現,啟動RpcBootstrap類,就會將rpc-server模塊寫的RPCServer和注入容器中。這個模塊相當於前面LBS大大圖片里的server端,真正提供服務的實現的模塊rpc-server模塊,相當於server stub,他負責將掃描到的被@RpcService注解的類注冊到zookeeper,對請求、返回值消息進行序列化、編解碼,處理請求等rpc-client模塊,就相當於client stub,他負責將client的請求進行編碼,發送出去,到zookeeper注冊中心去發現可用服務,進行調用rpc-register-zookeeper模塊,該模塊的ZookeeperServiceDiscovery類為rpc-client模塊提供服務發現功能。而zookeeperServiceRegistry類為rpc-server模塊提供了服務注冊的功能。其實就是實例化一個ZkClient對zookeeper服務進行訪問,然后對zookeeper進行注冊服務和發現服務rpc-common模塊,定義了一些工具類和消息對象。消息對象用於封裝請求和返回值消息,工具類主要用於序列化、編解碼等功能
最后,由於本人之前並沒有對netty和protobuf等工具有詳細了解,代碼細節無法詳述,這里只是簡單地歸納出該框架的幾點模塊關系以及作用
運行步驟
這里主要是本人運行該框架的幾個步驟,其實非常簡單
- 下載源碼到IDEA,地址
https://gitee.com/huangyong/rpc.git - 安裝、啟動zookeeper服務,不了解的童鞋可以參考 zookeeper安裝和使用 windows環境
- 啟動rpc-server,就是
rpc-sample-server下的RpcBootstrap類即可,這邊服務端就算啟用了

- 接下來運行
rpc-sample-client下面的幾個類,可以看到客戶端代碼其實是通過RpcProxy.create(...)獲取實例,並得到了返回值

小結
本文總結了RPC服務是做什么的以及RPC框架相關的幾個概念,接下來描述了如果我們自己寫RPC框架需要解決的幾個問題。並且使用@黃勇童鞋的開源demo,進行rpc使用的初體驗,勉強對rpc框架有了基本的認識。如果想深入使用、了解的童鞋可以嘗試使用阿里的hsf、谷歌的gRpc構建自己的微服務應用或者分布式計算等
