深入淺析Netty 在 Dubbo 中是如何應(yīng)用的
眾所周知,國(guó)內(nèi)知名框架 Dubbo 底層使用的是 Netty 作為網(wǎng)絡(luò)通信,那么內(nèi)部到底是如何使用的呢?今天我們就來(lái)一探究竟。
1. dubbo 的 Consumer 消費(fèi)者如何使用 Netty
注意:此次代碼使用了從 github 上 clone 的 dubbo 源碼中的 dubbo-demo 例子。
代碼如下:
System.setProperty("java.net.preferIPv4Stack", "true"); ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-consumer.xml"}); context.start(); // @1 DemoService demoService = (DemoService) context.getBean("demoService"); // get remote service proxy int a = 0; while (true) { try { Thread.sleep(1000); System.err.println( ++ a + " "); String hello = demoService.sayHello("world"); // call remote method System.out.println(hello); // get result } catch (Throwable throwable) { throwable.printStackTrace(); } }
當(dāng)代碼執(zhí)行到 @1 的時(shí)候,會(huì)調(diào)用 Spring 容器的 getBean 方法,而 dubbo 擴(kuò)展了 FactoryBean,所以,會(huì)調(diào)用 getObject 方法,該方法會(huì)創(chuàng)建代理對(duì)象。
這個(gè)過(guò)程中會(huì)調(diào)用 DubboProtocol 實(shí)例的 getClients(URL url) 方法,當(dāng)這個(gè)給定的 URL 的 client 沒(méi)有初始化則創(chuàng)建,然后放入緩存,代碼如下:
這個(gè) initClient 方法就是創(chuàng)建 Netty 的 client 的。
最終調(diào)用的就是抽象父類 AbstractClient 的構(gòu)造方法,構(gòu)造方法中包含了創(chuàng)建 Socket 客戶端,連接客戶端等行為。
public AbstractClient(URL url, ChannelHandler handler) throws RemotingException { doOpen(); connect(); }
doOpent 方法用來(lái)創(chuàng)建 Netty 的 bootstrap :
protected void doOpen() throws Throwable { NettyHelper.setNettyLoggerFactory(); bootstrap = new ClientBootstrap(channelFactory); bootstrap.setOption("keepAlive", true); bootstrap.setOption("tcpNoDelay", true); bootstrap.setOption("connectTimeoutMillis", getTimeout()); final NettyHandler nettyHandler = new NettyHandler(getUrl(), this); bootstrap.setPipelineFactory(new ChannelPipelineFactory() { public ChannelPipeline getPipeline() { NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyClient.this); ChannelPipeline pipeline = Channels.pipeline(); pipeline.addLast("decoder", adapter.getDecoder()); pipeline.addLast("encoder", adapter.getEncoder()); pipeline.addLast("handler", nettyHandler); return pipeline; } }); }
connect 方法用來(lái)連接提供者:
protected void doConnect() throws Throwable { long start = System.currentTimeMillis(); ChannelFuture future = bootstrap.connect(getConnectAddress()); boolean ret = future.awaitUninterruptibly(getConnectTimeout(), TimeUnit.MILLISECONDS); if (ret && future.isSuccess()) { Channel newChannel = future.getChannel(); newChannel.setInterestOps(Channel.OP_READ_WRITE); } }
上面的代碼中,調(diào)用了 bootstrap 的 connect 方法,熟悉的 Netty 連接操作。當(dāng)然這里使用的是 jboss 的 netty3,稍微有點(diǎn)區(qū)別。點(diǎn)擊這篇:教你用 Netty 實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 RPC。當(dāng)連接成功后,注冊(cè)寫(xiě)事件,準(zhǔn)備開(kāi)始向提供者傳遞數(shù)據(jù)。
當(dāng) main 方法中調(diào)用 demoService.sayHello(“world”) 的時(shí)候,最終會(huì)調(diào)用 HeaderExchangeChannel 的 request 方法,通過(guò) channel 進(jìn)行請(qǐng)求。
public ResponseFuture request(Object request, int timeout) throws RemotingException { Request req = new Request(); req.setVersion("2.0.0"); req.setTwoWay(true); req.setData(request); DefaultFuture future = new DefaultFuture(channel, req, timeout); channel.send(req); return future; }
send 方法中最后調(diào)用 jboss Netty 中繼承了 NioSocketChannel 的 NioClientSocketChannel 的 write 方法。完成了一次數(shù)據(jù)的傳輸。
2. dubbo 的 Provider 提供者如何使用 Netty
Provider demo 代碼:
System.setProperty("java.net.preferIPv4Stack", "true"); ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-provider.xml"}); context.start(); System.in.read(); // press any key to exit
Provider 作為被訪問(wèn)方,肯定是一個(gè) Server 模式的 Socket。如何啟動(dòng)的呢?
當(dāng) Spring 容器啟動(dòng)的時(shí)候,會(huì)調(diào)用一些擴(kuò)展類的初始化方法,比如繼承了 InitializingBean,ApplicationContextAware,ApplicationListener 。
而 dubbo 創(chuàng)建了 ServiceBean 繼承了一個(gè)監(jiān)聽(tīng)器。Spring 會(huì)調(diào)用他的 onApplicationEvent 方法,該類有一個(gè) export 方法,用于打開(kāi) ServerSocket 。
然后執(zhí)行了 DubboProtocol 的 createServer 方法,然后創(chuàng)建了一個(gè)NettyServer 對(duì)象。NettyServer 對(duì)象的 構(gòu)造方法同樣是 doOpen 方法和。
代碼如下:
protected void doOpen() throws Throwable { NettyHelper.setNettyLoggerFactory(); ExecutorService boss = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerBoss", true)); ExecutorService worker = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerWorker", true)); ChannelFactory channelFactory = new NioServerSocketChannelFactory(boss, worker, getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS)); bootstrap = new ServerBootstrap(channelFactory); final NettyHandler nettyHandler = new NettyHandler(getUrl(), this); channels = nettyHandler.getChannels(); bootstrap.setPipelineFactory(new ChannelPipelineFactory() { public ChannelPipeline getPipeline() { NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this); ChannelPipeline pipeline = Channels.pipeline(); pipeline.addLast("decoder", adapter.getDecoder()); pipeline.addLast("encoder", adapter.getEncoder()); pipeline.addLast("handler", nettyHandler); return pipeline; } }); channel = bootstrap.bind(getBindAddress()); }
該方法中,看到了熟悉的 boss 線程,worker 線程,和 ServerBootstrap,在添加了編解碼 handler 之后,添加一個(gè) NettyHandler,最后調(diào)用 bind 方法,完成綁定端口的工作。和我們使用 Netty 是一摸一樣。
3. 總結(jié)
可以看到,dubbo 使用 Netty 還是挺簡(jiǎn)單的,消費(fèi)者使用 NettyClient,提供者使用 NettyServer,Provider 啟動(dòng)的時(shí)候,會(huì)開(kāi)啟端口監(jiān)聽(tīng),使用我們平時(shí)啟動(dòng) Netty 一樣的方式。
而 Client 在 Spring getBean 的時(shí)候,會(huì)創(chuàng)建 Client,當(dāng)調(diào)用遠(yuǎn)程方法的時(shí)候,將數(shù)據(jù)通過(guò) dubbo 協(xié)議編碼發(fā)送到 NettyServer,然后 NettServer 收到數(shù)據(jù)后解碼,并調(diào)用本地方法,并返回?cái)?shù)據(jù),完成一次完美的 RPC 調(diào)用。
到此這篇關(guān)于深入淺析Netty 在 Dubbo 中是如何應(yīng)用的的文章就介紹到這了,更多相關(guān)Netty 在 Dubbo中應(yīng)用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring實(shí)戰(zhàn)之Bean的后處理器操作示例
這篇文章主要介紹了Spring實(shí)戰(zhàn)之Bean的后處理器操作,結(jié)合實(shí)例形式詳細(xì)分析了Bean的后處理器相關(guān)配置、操作方法及使用注意事項(xiàng),需要的朋友可以參考下2019-12-12java中HashMap.values()轉(zhuǎn)為ArrayList()問(wèn)題
這篇文章主要介紹了java中HashMap.values()轉(zhuǎn)為ArrayList()問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03mybatis中關(guān)于type-aliases-package的使用
這篇文章主要介紹了mybatis中關(guān)于type-aliases-package的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08springboot CommandLineRunner接口實(shí)現(xiàn)自動(dòng)任務(wù)加載功能
這篇文章主要介紹了springboot CommandLineRunner接口實(shí)現(xiàn)自動(dòng)任務(wù)加載功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05