Netty3之ServerBootstrap分析
為什么80%的碼農(nóng)都做不了架構(gòu)師?>>> ??
概述
ServerBootstrap是Netty提供的一個(gè)服務(wù)端工具類(lèi),通過(guò)設(shè)置ChanneFactory,ChannelPipelineFactory,用戶(hù)可以很方便的啟動(dòng)一個(gè)服務(wù)端。
ServerBootstrap是做什么的
ServerBootstrap是一個(gè)幫助類(lèi),用來(lái)創(chuàng)建服務(wù)端的Channel以監(jiān)聽(tīng)來(lái)自客戶(hù)端的連接。
面向連接
ServerBootstrap只支持面向連接的傳輸,比如TCP/IP,如果是沒(méi)有連接的傳輸,比如UDP/IP這樣,就需要使用ConnectionlessBootstrap。
Parent and Child Channel
ServerBootstrap通過(guò)bind()方法使用屬性ChannelFactory來(lái)創(chuàng)建服務(wù)端的Channel,以監(jiān)聽(tīng)并且accept客戶(hù)端的連接。服務(wù)端的Channel被稱(chēng)為parent Channel,來(lái)自客戶(hù)端的連接被稱(chēng)為child Channel。
配置Channel
使用Options來(lái)設(shè)置parent and child Channel的可選參數(shù),其中child Channel需要使用child.前綴。
ServerBootstrap b = ...;// Options for a parent channel b.setOption("localAddress", new InetSocketAddress(8080)); b.setOption("reuseAddress", true);// Options for its children b.setOption("child.tcpNoDelay", true); b.setOption("child.receiveBufferSize", 1048576);設(shè)置Channel Pipeline
Netty使用ChannelPipeline來(lái)處理和流轉(zhuǎn)IO事件。parent Channel的Pipeline是由ServerBootstrap內(nèi)部創(chuàng)建的,并且加入Binder的內(nèi)部類(lèi)作為ChannelHandler。我們可以設(shè)置ServerBootstrap的屬性parentHandler,一旦設(shè)置之后parentHandler就會(huì)被追加到parent Pipeline中用來(lái)我們自定義的ChannelHandler。
child Pipeline有兩種方式可以設(shè)置,一種方式是通過(guò)setPipelineFactory(ChannelPipelineFactory)設(shè)置,每個(gè)child Channel都會(huì)通過(guò)ChannelPipelineFactory創(chuàng)建新的ChannelPipeline,是推薦的方式。另一種方式通過(guò)setPipeline(ChannelPipeline)設(shè)置,這個(gè)方法內(nèi)部會(huì)創(chuàng)建ChannelPipelineFactory,復(fù)用設(shè)置的ChannelPipeline的內(nèi)部ChannelHandler。
不同配置,不同Channel
ServerBootstrap本身并不是占用資源,而是交給ChannelFactory來(lái)處理,官網(wǎng)文檔說(shuō)可以使用同一個(gè)ChannelFactory來(lái)創(chuàng)建多個(gè)ServerBootstrap,每個(gè)ServerBootstrap都可以使用不同的配置,從而創(chuàng)建不同的Channel(沒(méi)見(jiàn)過(guò)這種需要。)
ServerBootstrap創(chuàng)建和啟動(dòng)過(guò)程
Netty為我們提供ServerBootstrap作為工具來(lái)方便的啟動(dòng)服務(wù)端,使用起來(lái)也很簡(jiǎn)單,一般分為這幾步:
- 創(chuàng)建ChannelFactory
- 創(chuàng)建ChannelPipelineFactory
- 創(chuàng)建ServerBootstrap實(shí)例、設(shè)置ChannelFactory和ChannelPipelineFactory
- 調(diào)用bind方法
1、創(chuàng)建ChannelFactory
這個(gè)ChannelFactory在啟動(dòng)時(shí)候會(huì)創(chuàng)建parent Channel也就是服務(wù)的Channel以監(jiān)聽(tīng)來(lái)自客戶(hù)端的連接。
ChannelFactory是用來(lái)創(chuàng)建parent Channel
2、創(chuàng)建ChannelPipelineFactory
在accept Channel之后會(huì)使用這個(gè)ChannelPipelineFactory創(chuàng)建ChannelPipeline用來(lái)處理IO事件。常見(jiàn)的使用方法是使用Channels.pipeline()創(chuàng)建一個(gè)DefaultChannelPipeline,我們往里添加自定義的ChannelHandler即可。
ChannelPipelineFactory是用來(lái)服務(wù)child Channel
3、設(shè)置ChannelFactory和ChannelPipelineFactory
創(chuàng)建ServerBootstrap實(shí)例,設(shè)置ChannelFactory和ChannelPipelineFactory屬性。
4、調(diào)用bind()方法啟動(dòng),綁定在指定端口。
官網(wǎng)示例(也是常用使用方式,Dubbo就是這樣的,設(shè)計(jì)的簡(jiǎn)約而不簡(jiǎn)單)
// Configure the server. ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(Executors.newCachedThreadPool(),Executors.newCachedThreadPool()));// Set up the pipeline factory. bootstrap.setPipelineFactory(new ChannelPipelineFactory() {public ChannelPipeline getPipeline() throws Exception {return Channels.pipeline(new EchoServerHandler());} });// Bind and start to accept incoming connections. bootstrap.bind(new InetSocketAddress(port));bind()綁定過(guò)程
ServerBootstrap通過(guò)調(diào)用bind()方法來(lái)啟動(dòng)服務(wù)端。綁定方法很簡(jiǎn)單,首先創(chuàng)建parent的ChannelPipeline,然后使用ChannelFactory創(chuàng)建Channel,結(jié)束。
public Channel bind(final SocketAddress localAddress) {ChannelFuture future = bindAsync(localAddress);// Wait for the future.future.awaitUninterruptibly();if (!future.isSuccess()) {future.getChannel().close().awaitUninterruptibly();throw new ChannelException("Failed to bind to: " + localAddress, future.getCause());}return future.getChannel(); }public ChannelFuture bindAsync(final SocketAddress localAddress) {if (localAddress == null) {throw new NullPointerException("localAddress");}// 設(shè)置bossPipelineBinder binder = new Binder(localAddress);ChannelHandler parentHandler = getParentHandler();ChannelPipeline bossPipeline = pipeline();bossPipeline.addLast("binder", binder);if (parentHandler != null) {bossPipeline.addLast("userHandler", parentHandler);}// 創(chuàng)建parent ChannelChannel channel = getFactory().newChannel(bossPipeline);final ChannelFuture bfuture = new DefaultChannelFuture(channel, false);binder.bindFuture.addListener(new ChannelFutureListener() {public void operationComplete(ChannelFuture future) throws Exception {if (future.isSuccess()) {bfuture.setSuccess();} else {// Call close on bind failurebfuture.getChannel().close();bfuture.setFailure(future.getCause());}}});return bfuture; }分析方法過(guò)程:
Binder類(lèi)是ServerBootstrap內(nèi)部類(lèi),是一個(gè)UpstreamHandler,處理三個(gè)事件:channelOpen、childChannelOpen、exceptionCaught。
NioServerSocketChannelFactory創(chuàng)建NioServerSocketChannel,在NioServerSocketChannel的構(gòu)造函數(shù)中會(huì)觸發(fā)一個(gè)ChannelOpen的事件傳入Pipeline中,這個(gè)Pipeline就是在ServerBootstrap的bind()方法里創(chuàng)建的parent Pipeline。而ChannelOpen是一個(gè)UpstreamEvent,因此Binder類(lèi)就會(huì)執(zhí)行相應(yīng)的邏輯。
關(guān)鍵代碼
// NioServerSocketChannel的構(gòu)造函數(shù)中執(zhí)行 socket = ServerSocketChannel.open(); socket.configureBlocking(false); fireChannelOpen(this); // Binder類(lèi)的channelOpen方法,最后執(zhí)行channel的bind方法 evt.getChannel().bind(localAddress).addListener(new ChannelFutureListener() {public void operationComplete(ChannelFuture future) throws Exception {if (future.isSuccess()) {bindFuture.setSuccess();} else {bindFuture.setFailure(future.getCause());}} });// NioServerSocketChannel的bind方法 channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(channel, future, ChannelState.BOUND, localAddress));// NioServerSocketPipelineSink,調(diào)用NioServerBoss的bind方法 case BOUND:if (value != null) {((NioServerBoss) channel.boss).bind(channel, future, (SocketAddress) value);} else {((NioServerBoss) channel.boss).close(channel, future);}break;// NioServerBoss中創(chuàng)建RegisterTask void bind(final NioServerSocketChannel channel, final ChannelFuture future,final SocketAddress localAddress) {registerTask(new RegisterTask(channel, future, localAddress)); }// RegisterTask中執(zhí)行nio的bind和注冊(cè)到Selector,屬性的nio知識(shí)點(diǎn) channel.socket.socket().bind(localAddress, channel.getConfig().getBacklog()); bound = true;future.setSuccess(); fireChannelBound(channel, channel.getLocalAddress()); channel.socket.register(selector, SelectionKey.OP_ACCEPT, channel);流程分析:
總體的看來(lái),ServerBootstrap內(nèi)部將任務(wù)交給了Binder和ChannelFactory,進(jìn)一步說(shuō)其實(shí)是Channel來(lái)處理。通過(guò)一系列的事件觸發(fā),最終調(diào)用JDK NIO的ServerSocketChannel完成啟動(dòng)并注冊(cè)監(jiān)聽(tīng)。
ServerBootstrap沒(méi)做什么,都是ServerChannelFactory在處理,想要理清楚Netty的服務(wù)端,就得剖析NioServerSocketChannelFactory。
思考一下
為什么不直接在ServerBootstrap的bind方法里直接執(zhí)行Channel的bind方法呢?我覺(jué)得是想把復(fù)雜的邏輯剝離出來(lái)到Binder這個(gè)內(nèi)部類(lèi)里。may be
相關(guān)閱讀
NioServerSocketChannelFactory分析
轉(zhuǎn)載于:https://my.oschina.net/cregu/blog/2874332
總結(jié)
以上是生活随笔為你收集整理的Netty3之ServerBootstrap分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: echarts - 使用echarts过
- 下一篇: android中的相对路径