一、bind方法
详情请见以前的文章:Netty之Channel、NioEventLoopGroup、客户端connect方法总结
bind方法会调用initAndRegister方法
1.1 initAndRegister方法
1.1.1 生成channel的构造方法
- channel.newChannel()。通过反射生成了我们显式设置的NioSocketChannel.class对象,反射是用它的无参构造方法生成的。
- 无参构造方法级联调用了多个构造参数。在这个过程中,通过jdk自带的spi机制生成了原生nio的socketChannel对象,这个参数最终会在AbstractChannel的构造方法中设置到ch属性中,并且ch会设置成不阻塞
- 生成id,unsafe,pipeline等属性并赋值
1.1.2 init方法
- 将显示设置的handler配置到pipeline中
- 将显示配置的option设置到channel的pipeline中
- 将显示配置attr配置到channel中
1.1.3 register方法
这个方法在MultithreadEventLoopGroup类中。
- 选择一个EventLoop,调用其register方法
- 最终调用jdk原生nio的Channel.register(selector)
问题:服务端selector是在哪里生成的?
1.1.4 注册触发事件
- 调用ChannelInitializer.channelRegister()
- 内部调用了钩子方法initChannel(),这个就是我们自己写的initChannel()
二、new NioEventLoopGroup()
详情请见以前的文章:揭开BootStrap的神秘面纱
- 构造方法最终调用MultithreadEventExecutorGroup;
线程数nThreads如果没传,则设置为cpuNum*2;
children:他其实相当于线程池,存放EventLoop,类型为EventExecutor[]类型,数组大小就为nThreads; - 创建nThreads个EventLoop,放到children这个数组中
- 来看NioEventLoop构造方法:
①创建selector,selector是NioEventLoop的属性。
②将其他的值放到属性中,例如Executor等
上面的问题就得到解答了:
在 NioEventLoop构造方法的时候创建了selector
三、客户端发起请求
最终调用原生的channel.connect方法。
不解的地方:
不懂为什么要递归调用pipeline(TailContext、HeadContext)的connect方法?
四、ServerBootstrap
详情请见以前的文章:揭秘ServerBootstrap神秘面纱(服务端ServerBootstrap)
- NioServerSocketChannel的创建、服务端Channel(NioServerSocketChannel)的初始化、ChannelPipeline初始化、服务端注册到Selector与客户端过程大致相同
- 服务端有两个EventLoopGroup对象;
boossGroup-连接事件;workerGroup-io事件 - bossGroup不断监听客户端连接,当有新连接时,bossGroup就会为此连接初始化各项资源。然后从workerGroup中选中一个EventLoop绑定此连接,接下来的交互就全在此EventLoop中了
- 在启动时——bind()方法,用到的group为bossGroup
- workerGroup在哪与NioSocketChannel关联呢?——下面给出答案
问题:workerGroup在哪与NioSocketChannel关联呢?
ServerBootstrap中有channelRead方法,里面有childGroup.register(child)——child就是Channel。
- 在服务端调用ServerBootstrap.doBind方法时,会调用init()方法,ServerBootstrap重写了init()方法。
- 在这个重写的init方法里,为channel的pipeline添加了新的handler——ServerBootstrapAcceptor(ServerBootstrap的内部类)
- ServerBootstrapAcceptor的channelRead方法,在接收到连接事件就会触发,它包含了这段代码:
childGroup.register(child)...- 上述代码中childGroup就是workerGroup
4.1 handler
服务端有两个group:bossGroup和workerGroup。
猜想:bossGroup关心连接事件,workerGroup关心io事件。
关于这个猜想以后再验证
4.2 selector事件轮询
- doBind方法——>channel.eventLoop().excute()-SingleThreadEventExecutor.startThread()——>SingleThreadEventExecutor.this.run()
- 在上述run方法里,是一个死循环:轮询+事件+唤醒/休眠
- 原生nio的selector会有空轮询bug,它的selector.select()方法有问题,不该唤醒的时候有可能会一直唤醒