日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

RPC 【Remote Procedure Call】 原理

發(fā)布時(shí)間:2024/9/30 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 RPC 【Remote Procedure Call】 原理 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

轉(zhuǎn)載自:https://www.cnblogs.com/LBSer/p/4853234.html

? ? ? ?在學(xué)校期間大家都寫過不少程序,比如寫個(gè)hello world服務(wù)類,然后本地調(diào)用下,如下所示。這些程序的特點(diǎn)是服務(wù)消費(fèi)方和服務(wù)提供方是本地調(diào)用關(guān)系。

  而一旦踏入公司尤其是大型互聯(lián)網(wǎng)公司就會發(fā)現(xiàn),公司的系統(tǒng)都由成千上萬大大小小的服務(wù)組成,各服務(wù)部署在不同的機(jī)器上,由不同的團(tuán)隊(duì)負(fù)責(zé)。這時(shí)就會遇到兩個(gè)問題:1)要搭建一個(gè)新服務(wù),免不了需要依賴他人的服務(wù),而現(xiàn)在他人的服務(wù)都在遠(yuǎn)端,怎么調(diào)用?2)其它團(tuán)隊(duì)要使用我們的新服務(wù),我們的服務(wù)該怎么發(fā)布以便他人調(diào)用?下文將對這兩個(gè)問題展開探討。

public interface HelloWorldService {String sayHello(String msg); } public class HelloWorldServiceImpl implements HelloWorldService {@Overridepublic String sayHello(String msg) {String result = "hello world " + msg;System.out.println(result);return result;} } public class Test {public static void main(String[] args) {HelloWorldService helloWorldService = new HelloWorldServiceImpl();helloWorldService.sayHello("test");} }

1.?如何調(diào)用他人的遠(yuǎn)程服務(wù)?

? ? ? ? 由于各服務(wù)部署在不同機(jī)器,服務(wù)間的調(diào)用免不了網(wǎng)絡(luò)通信過程,服務(wù)消費(fèi)方每調(diào)用一個(gè)服務(wù)都要寫一坨網(wǎng)絡(luò)通信相關(guān)的代碼,不僅復(fù)雜而且極易出錯(cuò)。

  如果有一種方式能讓我們像調(diào)用本地服務(wù)一樣調(diào)用遠(yuǎn)程服務(wù),而讓調(diào)用者對網(wǎng)絡(luò)通信這些細(xì)節(jié)透明,那么將大大提高生產(chǎn)力,比如服務(wù)消費(fèi)方在執(zhí)行helloWorldService.sayHello("test")時(shí),實(shí)質(zhì)上調(diào)用的是遠(yuǎn)端的服務(wù)。這種方式其實(shí)就是RPC(Remote Procedure Call Protocol),在各大互聯(lián)網(wǎng)公司中被廣泛使用,如阿里巴巴的hsf、dubbo(開源)、Facebook的thrift(開源)、Google grpc(開源)、Twitter的finagle(開源)等。

  要讓網(wǎng)絡(luò)通信細(xì)節(jié)對使用者透明,我們需要對通信細(xì)節(jié)進(jìn)行封裝,我們先看下一個(gè)RPC調(diào)用的流程涉及到哪些通信細(xì)節(jié):

1)服務(wù)消費(fèi)方(client)調(diào)用以本地調(diào)用方式調(diào)用服務(wù);

2)client stub接收到調(diào)用后負(fù)責(zé)將方法、參數(shù)等組裝成能夠進(jìn)行網(wǎng)絡(luò)傳輸?shù)南Ⅲw;

3)client stub找到服務(wù)地址,并將消息發(fā)送到服務(wù)端;

4)server stub收到消息后進(jìn)行解碼;

5)server stub根據(jù)解碼結(jié)果調(diào)用本地的服務(wù);

6)本地服務(wù)執(zhí)行并將結(jié)果返回給server stub;

7)server stub將返回結(jié)果打包成消息并發(fā)送至消費(fèi)方;

8)client stub接收到消息,并進(jìn)行解碼;

9)服務(wù)消費(fèi)方得到最終結(jié)果。

? ? ??RPC的目標(biāo)就是要2~8這些步驟都封裝起來,讓用戶對這些細(xì)節(jié)透明。

1.1 怎么做到透明化遠(yuǎn)程服務(wù)調(diào)用?

  怎么封裝通信細(xì)節(jié)才能讓用戶像以本地調(diào)用方式調(diào)用遠(yuǎn)程服務(wù)呢?對java來說就是使用代理!java代理有兩種方式:1) jdk 動態(tài)代理;2)字節(jié)碼生成。盡管字節(jié)碼生成方式實(shí)現(xiàn)的代理更為強(qiáng)大和高效,但代碼維護(hù)不易,大部分公司實(shí)現(xiàn)RPC框架時(shí)還是選擇動態(tài)代理方式。

  下面簡單介紹下動態(tài)代理怎么實(shí)現(xiàn)我們的需求。我們需要實(shí)現(xiàn)RPCProxyClient代理類,代理類的invoke方法中封裝了與遠(yuǎn)端服務(wù)通信的細(xì)節(jié),消費(fèi)方首先從RPCProxyClient獲得服務(wù)提供方的接口,當(dāng)執(zhí)行helloWorldService.sayHello("test")方法時(shí)就會調(diào)用invoke方法。

public class RPCProxyClient implements java.lang.reflect.InvocationHandler{private Object obj;public RPCProxyClient(Object obj){this.obj=obj;}/*** 得到被代理對象;*/public static Object getProxy(Object obj){return java.lang.reflect.Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(), new RPCProxyClient(obj));}/*** 調(diào)用此方法執(zhí)行*/public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {//結(jié)果參數(shù);Object result = new Object();// ...執(zhí)行通信相關(guān)邏輯// ...return result;} } public class Test {public static void main(String[] args) {HelloWorldService helloWorldService = (HelloWorldService)RPCProxyClient.getProxy(HelloWorldService.class);helloWorldService.sayHello("test");} }

1.2 ?怎么對消息進(jìn)行編碼和解碼?

1.2.1 確定消息數(shù)據(jù)結(jié)構(gòu)

上節(jié)講了invoke里需要封裝通信細(xì)節(jié),而通信的第一步就是要確定客戶端和服務(wù)端相互通信的消息結(jié)構(gòu)。客戶端的請求消息結(jié)構(gòu)一般需要包括以下內(nèi)容:

1)接口名稱

  在我們的例子里接口名是“HelloWorldService”,如果不傳,服務(wù)端就不知道調(diào)用哪個(gè)接口了;

2)方法名

  一個(gè)接口內(nèi)可能有很多方法,如果不傳方法名服務(wù)端也就不知道調(diào)用哪個(gè)方法;

3)參數(shù)類型&參數(shù)值

  參數(shù)類型有很多,比如有bool、int、long、double、string、map、list,甚至如struct(class);

  以及相應(yīng)的參數(shù)值;

4)超時(shí)時(shí)間

5)requestID,標(biāo)識唯一請求id,在下面一節(jié)會詳細(xì)描述requestID的用處。

  同理服務(wù)端返回的消息結(jié)構(gòu)一般包括以下內(nèi)容。

1)返回值

2)狀態(tài)code

3)requestID?

1.2.2?序列化

  一旦確定了消息的數(shù)據(jù)結(jié)構(gòu)后,下一步就是要考慮序列化與反序列化了。

  什么是序列化?序列化就是將數(shù)據(jù)結(jié)構(gòu)或?qū)ο筠D(zhuǎn)換成二進(jìn)制串的過程,也就是編碼的過程。

  什么是反序列化?將在序列化過程中所生成的二進(jìn)制串轉(zhuǎn)換成數(shù)據(jù)結(jié)構(gòu)或者對象的過程。

  為什么需要序列化?轉(zhuǎn)換為二進(jìn)制串后才好進(jìn)行網(wǎng)絡(luò)傳輸嘛!

  為什么需要反序列化?將二進(jìn)制轉(zhuǎn)換為對象才好進(jìn)行后續(xù)處理!

  現(xiàn)如今序列化的方案越來越多,每種序列化方案都有優(yōu)點(diǎn)和缺點(diǎn),它們在設(shè)計(jì)之初有自己獨(dú)特的應(yīng)用場景,那到底選擇哪種呢?從RPC的角度上看,主要看三點(diǎn):1)通用性,比如是否能支持Map等復(fù)雜的數(shù)據(jù)結(jié)構(gòu);2)性能,包括時(shí)間復(fù)雜度和空間復(fù)雜度,由于RPC框架將會被公司幾乎所有服務(wù)使用,如果序列化上能節(jié)約一點(diǎn)時(shí)間,對整個(gè)公司的收益都將非常可觀,同理如果序列化上能節(jié)約一點(diǎn)內(nèi)存,網(wǎng)絡(luò)帶寬也能省下不少;3)可擴(kuò)展性,對互聯(lián)網(wǎng)公司而言,業(yè)務(wù)變化飛快,如果序列化協(xié)議具有良好的可擴(kuò)展性,支持自動增加新的業(yè)務(wù)字段,而不影響老的服務(wù),這將大大提供系統(tǒng)的靈活度。

  目前互聯(lián)網(wǎng)公司廣泛使用Protobuf、Thrift、Avro等成熟的序列化解決方案來搭建RPC框架,這些都是久經(jīng)考驗(yàn)的解決方案。

1.3 ?通信

  消息數(shù)據(jù)結(jié)構(gòu)被序列化為二進(jìn)制串后,下一步就要進(jìn)行網(wǎng)絡(luò)通信了。目前有兩種常用IO通信模型:1)BIO;2)NIO。一般RPC框架需要支持這兩種IO模型,原理可參考:一個(gè)故事講清楚NIO。

  如何實(shí)現(xiàn)RPC的IO通信框架呢?1)使用java nio方式自研,這種方式較為復(fù)雜,而且很有可能出現(xiàn)隱藏bug,但也見過一些互聯(lián)網(wǎng)公司使用這種方式;2)基于mina,mina在早幾年比較火熱,不過這些年版本更新緩慢;3)基于netty,現(xiàn)在很多RPC框架都直接基于netty這一IO通信框架,省力又省心,比如阿里巴巴的HSF、dubbo,Twitter的finagle等。

1.4 ?消息里為什么要有 requestID ?

? ? ? ?如果使用netty的話,一般會用channel.writeAndFlush()方法來發(fā)送消息二進(jìn)制串,這個(gè)方法調(diào)用后對于整個(gè)遠(yuǎn)程調(diào)用(從發(fā)出請求到接收到結(jié)果)來說是一個(gè)異步的,即對于當(dāng)前線程來說,將請求發(fā)送出來后,線程就可以往后執(zhí)行了,至于服務(wù)端的結(jié)果,是服務(wù)端處理完成后,再以消息的形式發(fā)送給客戶端的。于是這里出現(xiàn)以下兩個(gè)問題:

1)怎么讓當(dāng)前線程“暫停”,等結(jié)果回來后,再向后執(zhí)行?

2)如果有多個(gè)線程同時(shí)進(jìn)行遠(yuǎn)程方法調(diào)用,這時(shí)建立在client server之間的socket連接上會有很多雙方發(fā)送的消息傳遞,前后順序也可能是隨機(jī)的,server處理完結(jié)果后,將結(jié)果消息發(fā)送給client,client收到很多消息,怎么知道哪個(gè)消息結(jié)果是原先哪個(gè)線程調(diào)用的?

  如下圖所示,線程A和線程B同時(shí)向client socket發(fā)送請求requestA和requestB,socket先后將requestB和requestA發(fā)送至server,而server可能將responseA先返回,盡管requestA請求到達(dá)時(shí)間更晚。我們需要一種機(jī)制保證responseA丟給ThreadA,responseB丟給ThreadB。

怎么解決呢?

1)client線程每次通過socket調(diào)用一次遠(yuǎn)程接口前,生成一個(gè)唯一的ID,即requestID(requestID必需保證在一個(gè)Socket連接里面是唯一的),一般常常使用AtomicLong從0開始累計(jì)數(shù)字生成唯一ID;

2)將處理結(jié)果的回調(diào)對象callback,存放到全局ConcurrentHashMap里面put(requestID, callback);

3)當(dāng)線程調(diào)用channel.writeAndFlush()發(fā)送消息后,緊接著執(zhí)行callback的get()方法試圖獲取遠(yuǎn)程返回的結(jié)果。在get()內(nèi)部,則使用synchronized獲取回調(diào)對象callback的鎖,再先檢測是否已經(jīng)獲取到結(jié)果,如果沒有,然后調(diào)用callback的wait()方法,釋放callback上的鎖,讓當(dāng)前線程處于等待狀態(tài)。

4)服務(wù)端接收到請求并處理后,將response結(jié)果(此結(jié)果中包含了前面的requestID)發(fā)送給客戶端,客戶端socket連接上專門監(jiān)聽消息的線程收到消息,分析結(jié)果,取到requestID,再從前面的ConcurrentHashMap里面get(requestID),從而找到callback對象,再用synchronized獲取callback上的鎖,將方法調(diào)用結(jié)果設(shè)置到callback對象里,再調(diào)用callback.notifyAll()喚醒前面處于等待狀態(tài)的線程。

public Object get() {synchronized (this) { // 旋鎖while (!isDone) { // 是否有結(jié)果了wait(); //沒結(jié)果是釋放鎖,讓當(dāng)前線程處于等待狀態(tài)}}} private void setDone(Response res) {this.res = res;isDone = true;synchronized (this) { //獲取鎖,因?yàn)榍懊鎤ait()已經(jīng)釋放了callback的鎖了notifyAll(); // 喚醒處于等待的線程}}

2 如何發(fā)布自己的服務(wù)?

? ? ? ? 如何讓別人使用我們的服務(wù)呢?有同學(xué)說很簡單嘛,告訴使用者服務(wù)的IP以及端口就可以了啊。確實(shí)是這樣,這里問題的關(guān)鍵在于是自動告知還是人肉告知。

  人肉告知的方式:如果你發(fā)現(xiàn)你的服務(wù)一臺機(jī)器不夠,要再添加一臺,這個(gè)時(shí)候就要告訴調(diào)用者我現(xiàn)在有兩個(gè)ip了,你們要輪詢調(diào)用來實(shí)現(xiàn)負(fù)載均衡;調(diào)用者咬咬牙改了,結(jié)果某天一臺機(jī)器掛了,調(diào)用者發(fā)現(xiàn)服務(wù)有一半不可用,他又只能手動修改代碼來刪除掛掉那臺機(jī)器的ip。現(xiàn)實(shí)生產(chǎn)環(huán)境當(dāng)然不會使用人肉方式。

  有沒有一種方法能實(shí)現(xiàn)自動告知,即機(jī)器的增添、剔除對調(diào)用方透明,調(diào)用者不再需要寫死服務(wù)提供方地址?當(dāng)然可以,現(xiàn)如今zookeeper被廣泛用于實(shí)現(xiàn)服務(wù)自動注冊與發(fā)現(xiàn)功能!

  簡單來講,zookeeper可以充當(dāng)一個(gè)服務(wù)注冊表(Service Registry),讓多個(gè)服務(wù)提供者形成一個(gè)集群,讓服務(wù)消費(fèi)者通過服務(wù)注冊表獲取具體的服務(wù)訪問地址(ip+端口)去訪問具體的服務(wù)提供者。如下圖所示:

  具體來說,zookeeper就是個(gè)分布式文件系統(tǒng),每當(dāng)一個(gè)服務(wù)提供者部署后都要將自己的服務(wù)注冊到zookeeper的某一路徑上:?/{service}/{version}/{ip:port}, 比如我們的HelloWorldService部署到兩臺機(jī)器,那么zookeeper上就會創(chuàng)建兩條目錄:分別為/HelloWorldService/1.0.0/100.19.20.01:16888 ?/HelloWorldService/1.0.0/100.19.20.02:16888。

  zookeeper提供了“心跳檢測”功能,它會定時(shí)向各個(gè)服務(wù)提供者發(fā)送一個(gè)請求(實(shí)際上建立的是一個(gè) Socket 長連接),如果長期沒有響應(yīng),服務(wù)中心就認(rèn)為該服務(wù)提供者已經(jīng)“掛了”,并將其剔除,比如100.19.20.02這臺機(jī)器如果宕機(jī)了,那么zookeeper上的路徑就會只剩/HelloWorldService/1.0.0/100.19.20.01:16888。

  服務(wù)消費(fèi)者會去監(jiān)聽相應(yīng)路徑(/HelloWorldService/1.0.0),一旦路徑上的數(shù)據(jù)有任務(wù)變化(增加或減少),zookeeper都會通知服務(wù)消費(fèi)方服務(wù)提供者地址列表已經(jīng)發(fā)生改變,從而進(jìn)行更新。

  更為重要的是zookeeper與生俱來的容錯(cuò)容災(zāi)能力(比如leader選舉),可以確保服務(wù)注冊表的高可用性。

3 小結(jié)

RPC 幾乎是每一個(gè)從學(xué)校進(jìn)入互聯(lián)網(wǎng)公司的同學(xué)都要首先學(xué)習(xí)的框架,之前面試過一個(gè)在大型互聯(lián)網(wǎng)公司工作過兩年的同學(xué),對RPC 還是停留在使用層面,這是不應(yīng)該的,希望大家不僅要會用而且要知道內(nèi)部的原理。本文也僅是對RPC的一個(gè)比較粗糙的描述,希望對大家有所幫助,錯(cuò)誤之處也請指出修正。

4 一些開源的RPC框架

https://github.com/alibaba/dubbo

http://thrift.apache.org/?cm_mc_uid=87762817217214314008006&cm_mc_sid_50200000=1444181090

?

總結(jié)

以上是生活随笔為你收集整理的RPC 【Remote Procedure Call】 原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 涩涩在线播放 | 性色tv| 天天拍天天操 | 国产精品久久久久久69 | 久久咪咪 | 在线观看视频免费 | 久久久午夜电影 | 精品三级 | avtt久久 | 性调教学院高h学校 | 综合视频在线观看 | 伊人资源 | 国产av 一区二区三区 | 丝袜美腿亚洲综合 | 国产精品久久久久久精 | 爱操在线 | 中文字幕一区二区三区免费 | 在线观看免费成人 | 欧美精品第一页 | 日本高清视频在线观看 | 精品中文字幕在线 | 少妇太紧太爽又黄又硬又爽小说 | 三级影片在线免费观看 | 性做久久| 亚洲丝袜中文字幕 | 日韩超碰| 五月天激情小说 | 伊人影院亚洲 | 日韩国产欧美在线观看 | 免费在线中文字幕 | 麻豆av一区二区三区久久 | 国产8区| 曰韩一级片 | 91狠狠操| 少妇系列av | 插吧插吧综合网 | xxxxx色 | 午夜在线观看视频18 | 亚洲成人伊人 | 色中文字幕 | 日韩欧美成人一区二区 | 久久精彩视频 | 欧美成人aaaa | 国产精品18久久久久久无码 | gogo亚洲国模私拍人体 | 午夜激情影院 | 成人爱爱网站 | 欧美黑人性生活 | 久久女同 | 抽插丰满内射高潮视频 | 少妇愉情理伦片bd | 欧美黄频 | 久久人人爽人人爽人人av | 在线观看污网站 | 国产精品情侣自拍 | 动漫毛片 | 久久久电影 | 日本精品视频在线观看 | 色视频在线看 | 九月婷婷色 | 日韩精品一区二区三区在线观看 | 香蕉久久久久 | 天天看夜夜操 | 欧美日韩亚洲国产另类 | 边啃奶头边躁狠狠躁 | 成人在线免费视频 | 欧美 日韩 国产 成人 | 97精品国产97久久久久久粉红 | 人妻互换免费中文字幕 | 高清免费视频日本 | 久久六六 | 夜夜天天操 | 成人激情视频在线 | 日韩视频在线免费 | 国产毛片久久久久久久 | 男女视频在线免费观看 | 韩国性猛交╳xxx乱大交 | 亚洲综合视频在线观看 | 精品动漫一区二区三区在线观看 | 伦理欧美 | h无码动漫在线观看 | 国产高清区 | av一区二区在线观看 | 人妻精品一区一区三区蜜桃91 | 中国男女全黄大片 | 亚洲成人av综合 | 亚洲视频第一页 | 天天视频入口 | 国产福利免费观看 | 精品丰满少妇一区二区三区 | 女生和男生一起插插插 | 自拍偷拍激情小说 | 国产学生美女无遮拦高潮视频 | 4438x五月天| 免费的av片 | 97免费看 | 又黄又爽又刺激的视频 | 亚洲va韩国va欧美va精品 | 成人网色 |