Netty源码服务器启动流程

看到这篇文章的应该都用过Netty吧。Netty服务端的模板代码如下,我们分析下它是怎么启动的。不要纠结没有关闭连接的代码,毕竟我们只是用这段代码来debug。这篇文章我主要写的是Netty服务端的启动流程。

读完这篇文章你会知道:

  • Netty的几大组件的关系是什么?包括NioEventLoopGoup, NioEventLoop, Channle, ChannelHandler, Pipeline
  • Netty是怎么注册感兴趣的事件的?
  • Netty服务的bossGroup收到连接后,是怎么转派到workerGroup的。
  • Netty服务端启动时经历了什么?
package netty.simple;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class NettyServer {

    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();

        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 28)
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                .childHandler(new ChannelInitializer<SocketChannel>() {

                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        socketChannel.pipeline().addLast(new MyChatHandler());
                    }
                });
        ChannelFuture sync = bootstrap.bind(6666).sync();
        sync.channel().closeFuture().sync();
    }
}

EventLoopGroup事件循环组

Netty是基于Reactor事件模型的,它将通道的连接和处理分开,bossGroup负责处理NioServerSocketChannel通道连接,通道连接后生产NioSocketChannel分派到workerGroup处理通道的读写事件和业务逻辑。具体怎么通道怎么分配的,接下来再说明。

EventLoopGroup顾名思义,它就是EventLoop组,维护一堆EventLoop的,如下图(NioEventGroup实现了EventExcutor接口)。默认new EventLoopGroup()会生成(cpu核心数*2)个EventLoop,每个EventLoop绑定一个端口,所以如果服务只绑定一个端口的话,就指定为1个接口,即new EventLoopGroup(1);

quicker_2d9ab9a7-deb0-4ef4-a230-7d549ef8ae5a.png

NioEventLoop事件循环

每个EventLoop都有一个死循环,负责监听Channel上的事件。看NioEventLoop的继承链,它是一个EventExecutor、EventLoop,所以NioEventLoop是一个线程池。NioEventLoop的父类SingleThreadEventExecutor维护了一个线程池对象。

quicker_e527dc0a-cd93-444a-83a9-b2a57938435e.png

NioEventLoop的execute(Runable r)方法,这是线程次的提交方法,当提交一个线程到NioEventLoop中时,就是执行这个方法,它做的工作如下图,是将提交过来的线程task入队,然后执行NioEventLoop的线程提交方法execute,先将task入队,再看情况执行startThread()。

quicker_a7b027ff-e7f7-453d-a8a2-bcc170288b05.png

跟踪进去,它会执行doStartThread()方法。这个方法提交一个线程,立马执行了SingleThreadEventExecutor.this.run()方法。这里,虽然NioEventLoop没有继承Thread或者实现Runnable接口,不能认为它是一个线程实现类。但是它有自己的run()方法,并且初次启动NioEventLoop时会执行SingleThreadEventExecutor.this.run()方法,因此暂且可以认为它是一个线程吧。

quicker_db1d3441-3d5a-4923-b202-6237c5fca185.png

那NioEventLoop这个名义上的线程它的run方法是什么呢,如下图,他就是一个死循环了。说到这里,就知道问什么我们说NioEventLoop是一个死循环的线程了吧。

quicker_6f3f0e5b-fd81-458c-b9eb-42e4bb783504.png

Channel通道

再看到ChannelFuture f = b.bind(PORT).sync();这一句的bind方法,它做了初始化和注册通道的任务。即ServerBootstrap初始化了注册了通道。

quicker_95a2ed91-b18c-4556-bab1-2ef01443a210.png

他会根据.channel(NioServerSocketChannel.class)这句代码生成一个服务端的NioServerSocketChannel。

quicker_425eaf88-3f90-4126-b4e2-c2b6a09cd334.png

Channel初始化时,会给Channel塞很多东西,比如感兴趣的OP_ACCEPT事件,和非阻塞的配置,看到这里就明白了Netty底层还是NIO,Netty其实就是对NIO的封装了。

quicker_a67418a8-0745-4e71-8504-9b2efb8f7d01.png

quicker_096e46c5-2e44-4ae8-8fde-eaa49fdcd93f.png

还向该Channel的pipeline中加入了一个Channel初始化器ChannelInitializer,那么ChannelInitializer的initChannel什么时候执行呢?先剧透一下,这个方法在Channel注册完成后执行,现在还在Channel初始化阶段,还没执行。

quicker_a31b15d8-0906-431d-91b4-b38e9065eaba.png

Pipeline管道

写过Netty小demo的都知道,我们需要处理自己的业务逻辑时都是向Channel对应的pipeline中加入我们的ChannelHandler。这里就讲一下围绕Channel的组件吧。

如下图,每一个Channel都有自己所属的EventLoop和Pipeline。

quicker_073d9a21-6713-41fd-a33f-f6de019ee2c7.png

pipeline维护了链表形式的ChannelHandlerContext,每次addLast()添加ChannelHandler到pipeline中时,都会被转成ChannelHandlerContext并添加一个链表节点

quicker_b7df4071-be2d-4ef0-a09a-2b3faec380a2.png
quicker_598f9744-1bc2-4099-9ab0-a37ba006bf80.png

register注册

Channel初始化完成之后,会执行到注册阶段,如下代码,它提交了一个任务给到EventLoop,上面的分析我们知道,提交EventLoop.execute后,会先将task入队,然后启动死循环执行task,这里是register0方法。

quicker_710c1d40-8664-4a9f-bd95-866c2d346924.png

那么register0就是异步通过EventLoop执行的了。main线程继续执行,我们断点在了NioEventLoop的线程,切换过去可以发现它启动了死循环。

quicker_b07b256d-a2a3-47c6-904b-1a908e0aff2b.png
quicker_10eae2ca-d9c1-42cb-95fb-cec12078cfe2.png

调到底层进行注册了。

quicker_73d60f55-cdf6-45ec-a4d3-b9458ed64304.png

接下来看到没有,它要执行ChannelInitilizer的initChannel方法了。

quicker_bc1378d2-ea10-4911-b037-c9207bb7686d.png

它是怎么执行的呢,如下图,调用的是pipeline的execute方法。

quicker_026b4da5-1ee8-47fc-82cd-017e846c58be.png

跟踪进去,开始执行方法了。

quicker_d57299d1-a4dc-4d53-92ff-531958ff392e.png

跳到我们最初的initChannel方法。可以看到最后提交了一个task到EventLoop中。往pipeline中添加了一个ServerBootstrapAcceptor。bossGroup是怎么实现将连接建立后分配给workGroup的呢,这就是关键。

quicker_b52a035d-0f7b-48e9-a55a-83868de229c4.png

SocketChannel连接分配

我们知道,如果有客户端连接到服务端的话,会依次执行pipeline中的ChannelHandler,上面我们可以看到,NioServerSocketChannle最后一个ChannelHandler就是ServerBootstrapAcceptor连接分配器。

我们用客户端连接上来,发现它执行到了ServerBootstrapAcceptor的channelRead方法。并拿到一个SockerChannel,向SocketChannel添加了ChannelInitializer。然后向childGroup中注册该SocketChannel。

quicker_b302e290-7fa2-4682-afb5-e14119f2d97e.png

ChildGroup会选择一个EventLoop来注册这个SocketChannel。

quicker_83e35bb4-5a5e-45ff-94ca-4b0f5ba0171e.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,511评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,495评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,595评论 0 225
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,558评论 0 190
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,715评论 3 270
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,672评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,112评论 2 291
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,837评论 0 181
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,417评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,928评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,316评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,773评论 2 234
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,253评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,827评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,440评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,523评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,583评论 2 249