Netty启动源码解析

注册过程流程图

先解释一下各大实体类主要的关系

ServerBootStrap:启动类,设置最外层各种属性

NioEventLoopGroup:可以理解成一个线程池数组->NioEventLoop[nThreads];每个元素包含了一个线程池,boosGroup中的数组大小一般设置成1,在bossGroup中主要担任处理连接,在WorkerGroup中的作用主要是读写数据

NioEventLoop: 属性主要有线程池,selector,queue

        selector:可以注册多个channel以及指定channel感兴趣的事件,每个channel一定会有selector

        queue:任务队列

channel:管道,用于处理请求和收发数据,服务端启动之后会通过反射生成一个NioServerSocketChannel,客户端有注册请求的时候服务端的NioServerSocketChannel也会将客户端传过来的SocketChannel打包成一个NioSocketChannel

ChannelHandler:分为出栈(Out)和入栈(In)Handler,入栈代表处理请求接受数据,出栈代表发送数据返回响应,存活于channel中,channel通过责任链模式依次调用Handler

服务端代码:

 // 创建两个线程组bossGroup和workerGroup, 含有的子线程NioEventLoop的个数默认为cpu核数的两倍
        // bossGroup只是处理连接请求 ,真正的和客户端业务处理,会交给workerGroup完成
        EventLoopGroup bossGroup = new NioEventLoopGroup(3);
        EventLoopGroup workerGroup = new NioEventLoopGroup(8);
        try {
            // 创建启动对象
            ServerBootstrap bootstrap = new ServerBootstrap();
            // 服务端配置参数(链式编程)
            bootstrap.group(bossGroup, workerGroup) //设置两个线程组
                    .channel(NioServerSocketChannel.class)//传入Class文件,后续反射生成channel
                    // 初始化服务器连接队列大小,服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接。
                    // 多个客户端同时来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    //业务代码
                    .childHandler(new ChannelInitializer<SocketChannel>() {//创建通道初始化对象,设置初始化参数,在 SocketChannel 建立起来之前执行

                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //对workerGroup的SocketChannel设置处理器
                            ch.pipeline().addLast(new NettyServerHandler());
                        }
                    });
            ChannelFuture cf = bootstrap.bind(9000).sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

上述代码可以看到最外层的bootStrap通过链式调用设置了BossGroup和workerGroup,以及channel传入了一个NioServerSocketChannel.class的一个类,然后直接就调用bind启动,bind核心逻辑如下

启动流程:

netty的启动由bootstrap.bind(port)方法开始,此方法主要作用为:

        1.启动之前初始化各种属性,绑定至bootStrap

        2.通过传进来的NioServerSocketChannel.class反射生成Channel,并设置属性,并设置一个初始Handler

        3.NioEventLoop将channel注册到Selector的这个任务放入到task中

        4.select进入死循环第一次轮询事件(肯定为空),进入runAllTask方法, 取出步骤3的注册任务然后执行

        5.执行注册任务,核心代码 javaChannel().register(eventLoop().unwrappedSelector(), 0, this);

        6.执行2中加入的Handler(这个Handler主要是加入了一个ServerBootstrapAcceptor的Handler到channel中,用于客户端连接时生成NioServerSocketChannel用),然后通过责任链模式执行所有Handler中的channelActive方法

服务端启动的核心逻辑就是以上6步,可以找到对应的源码:

        1:初始化属性,举例channel.class

        通过追溯bootStrap.channel方法可以进到这里面,发现他通过反射将生成无参构造Channel的方法放到了一个属性constructor中(注意此时还没有执行这个方法)

2.通过反射生成无参Channel

通过追溯ServerBootstrap.bind(port)方法可以发现,在启动之初,便会调用1步骤中的反射生成Channel(就是服务端的ServerSocketChannel),并且会调用config().group().register(channel)方法将channel注册到config().group()->就是bossGroup中

3.继续追溯config().group().register(channel)方法

可以发现不管是同步或者是异步,都会调用redister0(promise)这个方法,但是异步调用是通过eventLoop.execute执行的,eventLoop属于BossGroup下,所以可以先看下execute的逻辑

可以发现异步调用的时候并非直接新起线程调用方法,而是先将他放入到一个task中,后续可能会从task中取出这个Runnable,然后再去执行

4.步骤3中可以发现将注册任务加入到了task中之后有调用了一个startThread方法

通过追溯上述方法,发现核心逻辑在SingleThreadEventExecutor.run()方法中

run方法中总共分为三大块,轮询客户端发过来的事件,然后processSelectedKeys处理发生的事件,但是在服务端启动的时候,这两块肯定是没有任何数据的,所以直接调用runAllTask方法

可以看到runTask方法中的逻辑就是不断的从queue中拿出task然后执行,所以代码将回到步骤3加入的register0(promise)这个方法中

5.doRegister()方法简单粗暴,直接就将channel注册到了eventLoop中

6.invokeHandlerIfNeeded方法最后会追到步骤2中init(channel)添加的Handler,然后执行其init方法,主逻辑就是往channel里面添加了一个ServerBootstrapAcceptor

pipeline.fireChannelRegistered()方法和pipeline.fireChannelActive()方法会通过责任链模式逐个调用Channel中的各个对应的方法->

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值