歡迎關注公衆號:【 愛編程】
若是有須要後臺回覆 2019贈送 1T的學習資料哦!!
本文是基於Netty4.1.36進行分析java
Netty服務端的啓動代碼基本都是以下:編程
private void start() throws Exception { final EchoServerHandler serverHandler = new EchoServerHandler(); /** * NioEventLoop並非一個純粹的I/O線程,它除了負責I/O的讀寫以外 * 建立了兩個NioEventLoopGroup, * 它們實際是兩個獨立的Reactor線程池。 * 一個用於接收客戶端的TCP鏈接, * 另外一個用於處理I/O相關的讀寫操做,或者執行系統Task、定時任務Task等。 */ EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup childGroup = new NioEventLoopGroup(); try { //ServerBootstrap負責初始化netty服務器,而且開始監聽端口的socket請求 ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, childGroup) .channel(NioServerSocketChannel.class) .localAddress(new InetSocketAddress(port)) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { // 爲監聽客戶端read/write事件的Channel添加用戶自定義的ChannelHandler socketChannel.pipeline().addLast(serverHandler); } }); ChannelFuture f = b.bind().sync(); f.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { bossGroup.shutdownGracefully().sync(); childGroup.shutdownGracefully().sync(); } }
從上圖的代碼能夠總結爲如下幾個步驟:緩存
一、建立ServerBootStrap實例
二、設置並綁定Reactor線程池:EventLoopGroup,EventLoop就是處理全部註冊到本線程的Selector上面的Channel
三、設置並綁定服務端的channel
四、五、建立處理網絡事件的ChannelPipeline和handler,網絡時間以流的形式在其中流轉,handler完成多數的功能定製:好比編解碼 SSl安全認證
六、綁定並啓動監聽端口
七、當輪訓到準備就緒的channel後,由Reactor線程:NioEventLoop執行pipline中的方法,最終調度並執行channelHandler安全
它就是主要引導啓動服務端,工做包括如下:服務器
流程:
首先從用戶代碼的bind()其實就是AbstractBootstrap.bind(),而後經過反射工廠將用戶經過b.channel(NioServerSocketChannel.class)傳入的NioServerSocketChannel經過調用底層的jdk的SelectorProvider建立channel,同時也接着建立好對應的ChannelPipeline。
詳情能夠參考下圖,本身去查看一下源碼:網絡
主要工做以下:異步
1)設置的option緩存到NioServerSocketChannelConfig裏
2)設置的attr設置到channel裏
3)保存配置的childOptions,配置的childAttrs 到ServerBootstrapAcceptor裏
4)往NioSocketChannel的pipeline中添加一個ServerBootstrapAcceptorsocket
主要的核心源碼以下:ide
@Override void init(Channel channel) throws Exception { final Map<ChannelOption<?>, Object> options = options0(); synchronized (options) { setChannelOptions(channel, options, logger); } final Map<AttributeKey<?>, Object> attrs = attrs0(); synchronized (attrs) { for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) { @SuppressWarnings("unchecked") AttributeKey<Object> key = (AttributeKey<Object>) e.getKey(); channel.attr(key).set(e.getValue()); } } ChannelPipeline p = channel.pipeline(); final EventLoopGroup currentChildGroup = childGroup; final ChannelHandler currentChildHandler = childHandler; final Entry<ChannelOption<?>, Object>[] currentChildOptions; final Entry<AttributeKey<?>, Object>[] currentChildAttrs; synchronized (childOptions) { currentChildOptions = childOptions.entrySet().toArray(newOptionArray(0)); } synchronized (childAttrs) { currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0)); } p.addLast(new ChannelInitializer<Channel>() { @Override public void initChannel(final Channel ch) throws Exception { final ChannelPipeline pipeline = ch.pipeline(); ChannelHandler handler = config.handler(); if (handler != null) { pipeline.addLast(handler); } ch.eventLoop().execute(new Runnable() { @Override public void run() { pipeline.addLast(new ServerBootstrapAcceptor( ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs)); } }); } }); }
小結:
整體如上面工做流程所述。
特別地建議:查看ServerBootstrapAcceptor源碼,你能夠發現ServerBootstrapAcceptor在channelRead事件觸發的時候(也就有客戶端鏈接的時候),把childHandler加到childChannel Pipeline的末尾,設置childHandler的options和attrs,最後把childHandler註冊進childGroupoop
註冊過程以下圖
小結:
Channel 註冊過程所作的工做就是將 Channel 與對應的 EventLoop 關聯。
1).每一個 Channel 都會關聯一個特定的 EventLoop, 而且這個 Channel 中的全部 IO 操做都是在這個 EventLoop 中執行的;
2).當關聯好 Channel 和 EventLoop 後, 會繼續調用底層的 Java NIO SocketChannel 的 register 方法, 將底層的 Java NIO SocketChannel 註冊到指定的 selector 中.
經過這兩步, 就完成了 Netty Channel 的註冊過程.
端口綁定的源碼流程基本以下圖,詳情能夠仍是你本身讀一下源碼比較好點。
小結:
其實netty端口綁定是調用 jdk的javaChannel().bind(localAddress, config.getBacklog());進行綁定,而後TCP鏈路創建成功,Channel激活事件,經過channelPipeline進行傳播。
客戶端啓動的常規代碼以下:
private void start() throws Exception { /** * Netty用於接收客戶端請求的線程池職責以下。 * (1)接收客戶端TCP鏈接,初始化Channel參數; * (2)將鏈路狀態變動事件通知給ChannelPipeline */ EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .remoteAddress(new InetSocketAddress(host,port)) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(new EchoClientHandler()); } }); //綁定端口 ChannelFuture f = b.connect().sync(); f.channel().closeFuture().sync(); } catch (Exception e) { group.shutdownGracefully().sync(); } }
1.用戶線程建立Bootstrap實例,經過API設置建立客戶端相關的參數,異步發起客戶端鏈接。
2.建立處理客戶端鏈接、I/O讀寫的Reactor線程組NioEventLoopGroup,默認爲CPU內核數的2倍。
3.經過Bootstrap的ChannelFactory和用戶指定的Channel類型建立用於客戶端NioSocketChannel,它的功能相似於JDK NIO類庫提供的SocketChannel
4.建立默認的Channel Handler Pipeline,用於調度和執行網路事件。
5.異步發起TCP鏈接,判斷鏈接是否成功。若是成功,則直接將NioSocketChannel註冊到多路複用器上,監聽讀操做位,用於數據包讀取和消息發送,若是沒有當即鏈接成功,則註冊鏈接監聽爲到多路複用器,等待鏈接結果。
6.註冊對應的網絡監聽狀態爲到多路複用器。
7.由多路複用器在I/O現場中輪詢個Channel,處理鏈接結果。
8.若是鏈接成功,設置Future結果,發送鏈接成功事件,觸發ChannelPipeline執行。
9.由ChannelPipeline調度執行系統和用戶的ChannelHandler,執行邏輯。
小結:
客戶端是如何發起 TCP 鏈接的?
以下圖:
特別提醒:
在AbstractChannelHandlerContext.connect()#findContextOutbound這步操做是返回的結果next實際上是頭節點,也就是說在下一步next.invokeConnect()這裏的next就是頭節點,因此最終是調用HeadContext .connect()
本文主要講述netty服務端和客戶端的簡單工做流程。
具體服務端與客戶端如何通訊,以及內存管理等方面的知識下一次再寫。
若是對 Java、大數據感興趣請長按二維碼關注一波,我會努力帶給大家價值。以爲對你哪怕有一丁點幫助的請幫忙點個贊或者轉發哦。
關注公衆號【愛編碼】,回覆2019有相關資料哦。