最近工作中由于涉及到微服務(wù)這塊无宿,所以把Dubbo又重新回顧了一遍,現(xiàn)在做個總結(jié)赂弓。
為什么使用RPC框架绑榴?
一個老生常談的問題,為什么使用RPC框架盈魁?如果你的系統(tǒng)是單機系統(tǒng)彭沼,完全可以不用考慮這個問題,但是這些年微服務(wù)盛行备埃,SOAP姓惑,企業(yè)級總線這些過于重量級,大廠都不一定能玩得轉(zhuǎn)的玩意按脚,所以何況是中小企業(yè)于毙。所以退而求其次選擇RESTful或者輕量級RPC框架,Dubbo之所以能在國內(nèi)脫穎而出辅搬,肯定是有它的原因唯沮,在這里我們就不詳細說了。
Dubbo能為我們做什么堪遂?
其實應(yīng)該說作為一款企業(yè)級RPC框架應(yīng)該具備哪些基本的功能:
1.基本的遠程調(diào)用
2.微服務(wù)管理
3.監(jiān)控功能
4.負載均衡
5.網(wǎng)關(guān)限流
6.高并發(fā)介蛉,高可用
基本的遠程調(diào)用
這個是我們今天要談的重點,Dubbo是怎么使用遠程調(diào)用的溶褪?
說到遠程調(diào)用币旧,我們可以采用HTTP,TCP/IP等協(xié)議,我們在大學(xué)的網(wǎng)絡(luò)課程里面學(xué)過猿妈,這些協(xié)議承載了太多內(nèi)容吹菱,很多東西都是我們不需要的,我們調(diào)用選擇一個方法其實只是傳幾個參數(shù)而已彭则,沒有必要用HTTP,TCP/IP傳輸太多我們不需要的內(nèi)容吧鳍刷,所以Dubbo選擇自己實現(xiàn)了自己的協(xié)議,那就是Dubbo協(xié)議俯抖。
現(xiàn)在協(xié)議有了输瓜,那么協(xié)議的內(nèi)容通過什么方式傳輸呢?既然HTTP,TCP/IP不能用芬萍,那么我們只能選擇socket尤揣,但是socket太低端,需要自己考慮的東西太多担忧,所以當(dāng)然是選擇業(yè)內(nèi)作為成熟的框架Netty芹缔。
那么Netty是如何實現(xiàn)網(wǎng)絡(luò)通信的坯癣?我們先看服務(wù)端實現(xiàn)代碼瓶盛,這個段代碼來自Dubbo的Netty Server 類doOpen方法
NettyHelper.setNettyLoggerFactory();
ExecutorService boss = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerBoss", true));
ExecutorService worker = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerWorker", true));
ChannelFactory channelFactory = new NioServerSocketChannelFactory(boss, worker, this.getUrl().getPositiveParameter("iothreads", Constants.DEFAULT_IO_THREADS));
this.bootstrap = new ServerBootstrap(channelFactory);
final NettyHandler nettyHandler = new NettyHandler(this.getUrl(), this);
this.channels = nettyHandler.getChannels();
this.bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
NettyCodecAdapter adapter = new NettyCodecAdapter(NettyServer.this.getCodec(), NettyServer.this.getUrl(), NettyServer.this);
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("decoder", adapter.getDecoder());
pipeline.addLast("encoder", adapter.getEncoder());
pipeline.addLast("handler", nettyHandler);
return pipeline;
}
});
this.channel = this.bootstrap.bind(this.getBindAddress());
如果你對netty不熟悉的話可能看不懂上面的代碼,我來解釋一下:
ServerBootstrap為netty的啟動類,傳入?yún)?shù)是ChannelFactory惩猫,這個類有兩個構(gòu)造函數(shù)芝硬,netty使用reactor模式進行網(wǎng)絡(luò)通信,我們可以看到傳了兩個線程池轧房,一個是boss,一個worker拌阴,顧名思義boss是老板,worker是打工仔奶镶,boss負責(zé)請求的接待迟赃,然后將工作分配給worker進行處理,最后一個參數(shù)是需要分配的io線程數(shù)的個數(shù)厂镇,這個是從URL里面獲取的纤壁,這個相當(dāng)于Dubbo的一個全局上下文,這個參數(shù)是netty 的workers的數(shù)量捺信,所有workers共享worker線程池酌媒,說白就是這些workers輪流去干活,干玩活把任務(wù)丟給worker線程池進行處理迄靠,有興趣的同學(xué)可以去看下Netty源代碼秒咨。
接著我們看到setPipelineFactory方法,這個是Netty內(nèi)部實現(xiàn)的管道機制掌挚,簡單來說就是服務(wù)端如果有數(shù)據(jù)處理就把數(shù)據(jù)丟到這些管道依次執(zhí)行雨席,nettyHandler是Dubbo實現(xiàn)的一個管道處理方法。這些細節(jié)其實我們不用關(guān)注太多吠式,其實說來就是Netty幫我們創(chuàng)建了一個 socket鏈接舅世,如果有請求連接進來,或者有數(shù)據(jù)進來奇徒,我們只需要關(guān)注具體的實現(xiàn)業(yè)務(wù)邏輯雏亚,其它方面Netty已經(jīng)幫我們處理好了。
說到了服務(wù)端的實現(xiàn)摩钙,那么客戶端是如何做的呢罢低?
聰明的你可能已經(jīng)想到,既然有NettyServer胖笛,那么肯定有NettyClient网持,沒錯!下面我們來看NettyClient的doOpen方法實現(xiàn):
NettyHelper.setNettyLoggerFactory();
this.bootstrap = new ClientBootstrap(channelFactory);
this.bootstrap.setOption("keepAlive", true);
this.bootstrap.setOption("tcpNoDelay", true);
this.bootstrap.setOption("connectTimeoutMillis", this.getTimeout());
final NettyHandler nettyHandler = new NettyHandler(this.getUrl(), this);
this.bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
NettyCodecAdapter adapter = new NettyCodecAdapter(NettyClient.this.getCodec(), NettyClient.this.getUrl(), NettyClient.this);
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("decoder", adapter.getDecoder());
pipeline.addLast("encoder", adapter.getEncoder());
pipeline.addLast("handler", nettyHandler);
return pipeline;
}
});
注意以上代碼是基于2.8.4长踊,對于最新的Dubbo可能不是這樣功舀,但是原理是一樣的,如果你有理解上面Server端的初始化過程身弊,那么這段代碼應(yīng)該不難看懂辟汰。
所以RPC不過如此吧列敲,但是這只是Dubbo的冰山一腳,本篇文章只是為了說明原理帖汞,列舉了底層核心代碼而已戴而,在實現(xiàn)方法Dubbo使用了CompleteFuture來實現(xiàn)異步調(diào)度。
其它方面
Dubbo作為一款企業(yè)級別的框架翩蘸,當(dāng)然不會只能上面兩段代碼解決所有問題所意,為了考慮擴展性,Dubbo提供了注冊中心催首,以及自己一套URL的上下文傳輸機制扶踊,簡單來說URL就是DubboInvoker,DubboInvoker就是Dubbo的核心郎任,說白了姻檀,DubboInvoker就是封裝上面兩端代碼,使RPC使用對我們開發(fā)人員透明涝滴,我們不用去關(guān)注底層的網(wǎng)絡(luò)傳輸绣版,序列化,就是代理調(diào)用機制歼疮。為了實現(xiàn)擴展Dubbo參考TCP/IP協(xié)議杂抽,實現(xiàn)了Exchanger和Transporter兩層,剛才列舉上面6點中的第5點是在這兩層實現(xiàn)的韩脏。
技術(shù)方面缩麸,Dubbo實現(xiàn)了自己的IOC機制SPI擴展,為了實現(xiàn)高并發(fā)赡矢,Dubbo當(dāng)然會實現(xiàn)自己的線程調(diào)度機制以及高效的時間輪算法杭朱,具體可以參考我之前寫的《Dubbo的線程模型》。
至于負載均衡吹散,集群弧械,以及配置中心的實現(xiàn)也是其可圈可點的地方,有機會我下次再分享吧空民,今天主要是介紹Dubbo的底層原理刃唐。