Netty是对Java nio的封装,回顾后者,A、一个ServerSocketChannel和一个Selector,B、把ServerSocketChannel bind到一个端口上,C、再把ServerSocketChannel register到Selector上,在while循环中,D、Selector检测ServerSocketChannel 有数据到来(握手请求),调用accept,F、生成一个SockectChannel,也注册到selector上。Selector如果检测到Selector有数据到来,调用channel.read读取数据,而写数据是调用channel.write。

再到netty中一个一个的找,启动类是ServerBootstrap,关键方法是bind,点进去看,

1、initAndRegister方法,用channelFactory新建一个channel,这个channel是上面代码中channel()方法的参数NioServerSocketChannel的实例(A)。然后调用group的register方法,具体实现在NioEventLoopGroup的父类MultithreadEventLoopGroup中,调用的是next().register,这个next是按轮询的方式取出group的child数组的元素,数组中存的是NioEventLoop,具体的初始化是在NioEventLoopGroup的构造方法中,而且每一个NioEventLoop对应一个selector。register的具体实现在NioEventLoop的父类SingleThreadEventLoop中,调用的channel的unsafe属性的register,而NioServerSocketChannel的unsafe是其父类AbstractNioMessageChannel的newSafe方法的返回值NioMessageUnsafe,其register方法在父类AbstractUnsafe中,点进去register0方法中的doRegister方法,具体实现是在AbstractNioChannel中,这个是内部类对外部类的引用,发现有这一句:javaChannel().register(eventLoop().unwrappedSelector(), 0, this)(C),javaChannel.register就是取出ServerSocketChannel,调用其register方法,eventLoop().unwrappedSelector()这个就是loopGroup的loop数组中,loop--线程--selector是一对一的关系,而这几个“一”和channel是一对多或者一对一关系,可以发现netty底层其实还是Java的nio。

2、再往下看,doBind0方法,eventloop.execute方法中有个channel.bind,调用的是pipeline.bind,pipeline是channel的属性,掌管这一个双向链表head到tail,中间可以放channel自己的handler,这是个责任链模式,channel现在的bind以及以后读取到数据都是从这个链表的正向或者逆向流动处理一遍,最后是head的bind,最后还是unsafe调用nioserverSOcketChannel的bind,里面是nio channel的bind+listen(B)。

3、eventloop.execute方法会保证对应的线程启动,线程的run方法会同时执行task,以及进行对应的selector的select,像nio那样,遍历所有key,检测可读,可写,有链接到来等情况,具体方法在processSelectedKeys。由于现在我们讨论的是NioServerSocketChannel,所以看OP_ACCEPT也就是有连接到来的情况,调用的是unsafe.read()。上面说过这是个NioMessageUnsafe,点进去看其read方法,核心在doReadMessages(readBuf),具体实现由回到NioServerSocketChannel,发现直接调用熟悉的SocketChannel ch = SocketUtils.accept(javaChannel()),也就是Java NIO的ServerSocketChannel的accept方法,得到socketChannel,并放在readBuf中,在下面的pipeline.fireChannelRead方法中再进行遍历,上面说过,这个是在handler链中流动,按照Java NIO的流程,这里应该是让新得到的socketChannel注册到某个selector上,以后selector就遍历这些channel,进行实际数据的读取。那么现在pipeline中除了tail和head这两个空handler,具体的处理在一个ServerBootstrap的内部类ServerBootstrapAcceptor中,这个ServerBootstrapAcceptor之所以能存在于pipeline中,是因为刚才说的initAndRegister方法中,channel生成之后有一个init(channel)方法,往pipeline中添加了一个ChannelInitializer,这也是一个ChannelHandler。而在上面说的doRegister方法之后,有一个pipeline.fireChannelRegistered(),把管道中的所有handler都调用channelRegistered方法,而ChannelInitializer的channelRegistered调用了initChannel方法,这样就把ServerBootstrapAcceptor放入NioServerSocketChannel的管道中,点击去看其channelRead方法,有childGroup.register(child),这个和之前的group.register道理一样,调用的是NioSocketChannel的unsafe:NioByteUnsafe的父类,同样是AbstractUnsafe的register方法,也是从childLoopGroup这个子线程组中取出一个loop,把NioSocketChannel注册到对应的selector上(D),线程的run方法for循环中读取请求数据,在其pipeline中执行channelRead方法,我们只需要把我们自己的handler放入pipelin就可以进行我们自己的逻辑(E)。到这里,数据流动大体上是通了。

4、writeAndFlush方法是向channel写数据的方法,我们知道netty是对java nio的封装,而nio只有write没有flush,那么netty的writeAndFlush是什么呢?逻辑是channel.writeAndFlush--->pipeline.writeAndFlush--->tail.writeAndFlush,到这里分成两个方法:invokeWrite0 和 invokeRead0。从tail开始经过责任链直到head(一般来讲,中间的handler只是处理msg,最后的向内核空间写数据还是在headContext)。先看invokeWrite0,到了headContext.write--->unsafe的write--->outboundBuffer.addMessage方法把msg放到封装成entry,放到链表的最后,也就是说write方法并不调用javaChannel的write向内核写数据。再看invokeFlush0,到了headContext.flush--->unsafe.flush()--->flush0--->NioSocketChannel.dowrite--->ch.write(nioBuffer),最后调用封装的JavaChannel的write方法写到内核空间。还有一个要注意的就是,比如rocketMQ源码中channel.writeAndFlush的参数是Object类型,而headContext接受的需要是ByteBuf类型,这是因为在tail到head的过程中,有encoder类型的channel处理Object。

4、具体的解码编码,粘包的处理,待完善。