Netty分布式server啟動流程N(yùn)io創(chuàng)建源碼分析
前文傳送門 Netty分布式Server啟動流程服務(wù)端初始化源碼分析
NioServerSocketChannel創(chuàng)建
我們?nèi)绻煜io, 則對channel的概念則不會陌生, channel在相當(dāng)于一個通道, 用于數(shù)據(jù)的傳輸
Netty將jdk的channel進(jìn)行了包裝, 并為其擴(kuò)展了更多的功能
在netty中也分為服務(wù)端channel和客戶端channel, 在Nio模式下, 服務(wù)端channel對應(yīng)的類為NioServerSocketChannel, 包裝的jdk的ServerSocketChannel
客戶端channel對應(yīng)的類為NioSocketChannel, 所包裝的jdk的類為SocketChannel
繼承關(guān)系
最簡單的繼承關(guān)系如下(經(jīng)簡化):
我們繼續(xù)看第一小節(jié)demo:
//創(chuàng)建boss和worker線程(1) EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); //創(chuàng)建ServerBootstrap(2) ServerBootstrap b = new ServerBootstrap(); //初始化boss和work線程化兩個線程(3) b.group(bossGroup, workerGroup) //聲明NioServerSocketChannel(4) .channel(NioServerSocketChannel.class) //初始化客戶端Handler(5) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) { ch.pipeline().addLast(new StringDecoder()); ch.pipeline().addLast(new StringEncoder()); ch.pipeline().addLast(new ServerHandler()); } }); //綁定端口(6) ChannelFuture f = b.bind(8888).sync(); f.channel().closeFuture().sync();
綁定端口
我們繼續(xù)看第六步, 綁定端口:
ChannelFuture f = b.bind(8888).sync();
在此, 我們看到綁定了8888端口
我們跟到bind(8888)方法中:
public ChannelFuture bind(int inetPort) { return bind(new InetSocketAddress(inetPort)); }
端口封裝成socket地址對象
繼續(xù)跟bind方法:
public ChannelFuture bind(SocketAddress localAddress) { validate(); return doBind(localAddress); }
validate()做了一些屬性驗(yàn)證
我們繼續(xù)跟到doBind(localAddress)方法:
private ChannelFuture doBind(final SocketAddress localAddress) { //初始化并注冊(1) final ChannelFuture regFuture = initAndRegister(); //獲得channel(2) final Channel channel = regFuture.channel(); if (regFuture.cause() != null) { return regFuture; } if (regFuture.isDone()) { ChannelPromise promise = channel.newPromise(); //綁定(3) doBind0(regFuture, channel, localAddress, promise); return promise; } else { //去除非關(guān)鍵代碼 return promise; } }
去除了一些非關(guān)鍵的代碼, 重點(diǎn)關(guān)注注釋標(biāo)注的第一步, 初始化并注冊:
final ChannelFuture regFuture = initAndRegister();
跟進(jìn)initAndRegister()方法 創(chuàng)建channel
final ChannelFuture initAndRegister() { Channel channel = null; try { //創(chuàng)建channel channel = channelFactory.newChannel(); init(channel); } catch (Throwable t) { //忽略非關(guān)鍵代碼 } ChannelFuture regFuture = config().group().register(channel); //忽略非關(guān)鍵代碼 return regFuture; }
關(guān)注添加注釋的步驟, 創(chuàng)建channel, 這一點(diǎn)也是我們這節(jié)需要講明白的內(nèi)容
看這一步:
channel = channelFactory.newChannel();
這里channelFactory調(diào)用了newChannel()的這個方法, 這個方法從名字就不難理解, 是新建一個channel, 回顧下上一小節(jié), 這個channelFactory是在哪里初始化呢?
根據(jù)上一小節(jié)代碼, channelFactory是在Bootstrap的channelFactory ()方法初始化的:
public B channelFactory(ChannelFactory<? extends C> channelFactory) { this.channelFactory = channelFactory; return (B) this; }
而這個方法又是channel()方法中調(diào)用的:
public B channel(Class<? extends C> channelClass) { return channelFactory(new ReflectiveChannelFactory<C>(channelClass)); }
這里傳入ReflectiveChannelFactory對象就是初始化的channelFactory對象
所以newChannel()是調(diào)用ReflectiveChannelFactory對象的newChannel方法
跟到ReflectiveChannelFactory對象的newChannel方法中:
@Override public T newChannel() { try { return clazz.newInstance(); } catch (Throwable t) { throw new ChannelException("Unable to create Channel from class " + clazz, t); } }
我們看到這個clazz對象通過反射創(chuàng)建了channel, 這個clazz對象, 就是我們上一節(jié)提到過的, 初始化的NioServerSocketChannel的class對象
這里通過反射調(diào)用, 會創(chuàng)建一個NioServerSokectChannel
學(xué)習(xí)過nio的小伙伴都知道jdk的ServerSocketChannel, 用于接受鏈接事件, 而netty的NioServerSocketChannel是和jdk的channel有什么關(guān)系呢?
實(shí)際上netty的channel和jdk的channel的含義一樣, 也是一個通道, 只是netty為其做了擴(kuò)展, 而channel的事件處理, 也是通過jdk的channel去做的, 我們跟隨著NioServerSocketChannel的創(chuàng)建過程, 來了解他們之間的關(guān)聯(lián)關(guān)系
clazz.newInstance()通過反射創(chuàng)建一個NioServerSocketChannel對象, 首先會走到NioServerSocketChannel的構(gòu)造方法, 我們跟到他的構(gòu)造方法, 查看NioServerSocketChannel的創(chuàng)建過程
首先會調(diào)用它的無參構(gòu)造方法:
public NioServerSocketChannel() { this(newSocket(DEFAULT_SELECTOR_PROVIDER)); }
我們看到這個構(gòu)造方法調(diào)用了另一個有參的構(gòu)造方法, 傳入?yún)?shù)是 newSocket(DEFAULT_SELECTOR_PROVIDER)
我們首先看DEFAULT_SELECTOR_PROVIDER這個這變量:
private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
看到這初始化了一個SelectorProvider對象, 而這個對象是通過靜態(tài)方法provider()創(chuàng)建的, SelectorProvider對象可以用于創(chuàng)建jdk底層的ServerSocketChannel
我們繼續(xù)跟到newSocket(DEFAULT_SELECTOR_PROVIDER)中:
private static ServerSocketChannel newSocket(SelectorProvider provider) { return provider.openServerSocketChannel(); }
去掉try-catch塊, 發(fā)現(xiàn)這個方法是通過SelectorProvider對象的openServerSocketChannel()方法創(chuàng)建一個jdk底層的ServerSocketChannel, 至此我們可以知道, 與NioServerSokectChannel綁定的jdk底層的ServerSocketChannel就是這么創(chuàng)建的
父類的構(gòu)造方法
那么創(chuàng)建之后如何與netty的channel綁定?繼續(xù)跟代碼
跟到this(newSocket(DEFAULT_SELECTOR_PROVIDER))中:
public NioServerSocketChannel(ServerSocketChannel channel) { super(null, channel, SelectionKey.OP_ACCEPT); config = new NioServerSocketChannelConfig(this, javaChannel().socket()); }
我們看到這里調(diào)用了父類的構(gòu)造方法,繼續(xù)往里跟:
protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) { super(parent, ch, readInterestOp); }
這里調(diào)用了其父類AbstractNioMessageChannel類的構(gòu)造方法, AbstractNioMessageChannel這個類同學(xué)們請記住, 有關(guān)是NioServerSocketChannel的父類, 代表著服務(wù)端channel的相關(guān)屬性和操作, 之后有關(guān)服務(wù)端channel的一些事件會在這個類中完成
我們看到這個類的構(gòu)造方法中又調(diào)用了它的父類的構(gòu)造方法, 我們繼續(xù)跟:
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) { super(parent); //保存channel this.ch = ch; //綁定事件 this.readInterestOp = readInterestOp; try { //設(shè)置為非阻塞 ch.configureBlocking(false); } catch (IOException e) { //去掉非關(guān)鍵代碼 } }
這里又調(diào)用了其父類AbstractChannel的構(gòu)造方法, 跟進(jìn)去這個方法之前, 我們先往下看
首先看這一步:
this.ch = ch;
這步就是綁定jdk底層的ServerSocketChannel, 至此我們知道, jdk的channel和netty定義的channel是組合關(guān)系, netty的channel中有個jdk的channel的成員變量, 而這個成員變量就定義在AbstractNioChannel這個類當(dāng)中, 希望同學(xué)們將這個結(jié)論牢牢記住, 對以后的學(xué)習(xí)很有幫助
將jdk的channel設(shè)置為非阻塞模式
我們看到后面的這一步:
ch.configureBlocking(false);
這一步, 就是將jdk的channel設(shè)置為非阻塞模式, 這里熟悉Nio的同學(xué)應(yīng)該不會陌生, 這里不再贅述
我們繼續(xù)跟到super(parent)中, 走到其父類AbstractChannel的構(gòu)造方法:
protected AbstractChannel(Channel parent) { this.parent = parent; id = newId(); unsafe = newUnsafe(); pipeline = newChannelPipeline(); }
首先看下這個parent, 這個parent是NioServerSocketChannel調(diào)用其父類構(gòu)造方法傳入的, 傳入的是null, 所以這一步AbstractChannel的屬性parent也是null, 這個parent, 我們之后再講客戶端channel的時候會講到
id = newId()是為每個channel創(chuàng)建一個唯一id
我們重點(diǎn)關(guān)注下后兩步:
unsafe = newUnsafe(); pipeline = newChannelPipeline();
這里初始化了兩個屬性unsafe, 和pipeline, 目前我們只需要知道這兩個屬性是在這里初始化的, 至于這兩個屬性的概念, 后面的章節(jié)會講到
以上就是創(chuàng)建NioServerSocketChannel的過程, 同學(xué)們可以課后跟進(jìn)源碼去熟悉鞏固
以上就是Netty分布式server啟動流程N(yùn)io創(chuàng)建源碼分析的詳細(xì)內(nèi)容,更多關(guān)于Netty分布式server啟動流程N(yùn)io創(chuàng)建的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java泛型枚舉Annotation接口詳細(xì)解讀與Eclipse發(fā)展
這篇文章主要給大家介紹了關(guān)于Java中方法使用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-08-08SpringBoot接口返回結(jié)果封裝方法實(shí)例詳解
在實(shí)際項(xiàng)目中,一般會把結(jié)果放在一個封裝類中,封裝類中包含http狀態(tài)值,狀態(tài)消息,以及實(shí)際的數(shù)據(jù)。這里主要記錄兩種方式,通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧2021-09-09java實(shí)現(xiàn)投票程序設(shè)計(jì)
這篇文章主要介紹了java實(shí)現(xiàn)投票程序設(shè)計(jì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2015-12-12Spring?Boot在啟動時執(zhí)行一次的功能實(shí)現(xiàn)
這篇文章主要給大家介紹了關(guān)于Spring?Boot在啟動時執(zhí)行一次的功能實(shí)現(xiàn),在實(shí)習(xí)過程中,有時候會遇到一些項(xiàng)目啟動初始化的需求,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-08-08使用ShardingJDBC進(jìn)行數(shù)據(jù)分片以及讀寫分離
ShardingJDBC是一個輕量級的Java框架,提供了數(shù)據(jù)分片、讀寫分離、分布式主鍵生成等數(shù)據(jù)訪問功能,本文將給大家介紹如何使用ShardingJDBC進(jìn)行數(shù)據(jù)分片以及讀寫分離,需要的朋友可以參考下2024-01-01Java利用Strategy模式實(shí)現(xiàn)堆排序
策略設(shè)計(jì)模式(Strategy):可以整體的替換一個算法的實(shí)現(xiàn)部分,能夠整體的替換算法,能讓我們輕松地用不同方法解決同一個問題。本文將利用Strategy模式實(shí)現(xiàn)堆排序,感興趣的可以學(xué)習(xí)一下2022-09-09詳解SpringBoot+Dubbo集成ELK實(shí)戰(zhàn)
這篇文章主要介紹了詳解SpringBoot+Dubbo集成ELK實(shí)戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10SpringBoot集成Nacos的項(xiàng)目實(shí)踐
本文主要介紹了SpringBoot集成Nacos的項(xiàng)目實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07