找回密码
 立即注册
首页 业界区 业界 Netty源码—4.客户端接入流程

Netty源码—4.客户端接入流程

蔬陶 5 天前
大纲
1.关于Netty客户端连接接入问题整理
2.Reactor线程模型和服务端启动流程
3.Netty新连接接入的整体处理逻辑
4.新连接接入之检测新连接
5.新连接接入之创建NioSocketChannel
6.新连接接入之绑定NioEventLoop线程
7.新连接接入之注册Selector和注册读事件
8.注册Reactor线程总结
9.新连接接入总结
 
1.关于Netty客户端连接接入问题整理
一.Netty是在哪里检测有新连接接入的?
答:boss线程第一个过程轮询出ACCEPT事件,然后boss线程第二个过程通过JDK底层Channel的accept()方法创建一条连接。
 
二.新连接是怎样注册到NioEventLoop线程的?
答:boss线程调用chooser的next()方法拿到一个NioEventLoop,然后将新连接注册到NioEventLoop的Selector上。
 
2.Reactor线程模型和服务端启动流程
(1)Netty中的Reactor线程模型
(2)服务端启动流程
 
(1)Netty中的Reactor线程模型
Netty中最核心的是两种类型的Reactor线程,这两种类型的Reactor线程可以看作Netty中的两组发动机,驱动着Netty整个框架的运转。一种类型是boss线程,专门用来接收新连接,然后将连接封装成Channel对象传递给worker线程。另一种类型是worker线程,专门用来处理连接上的数据读写。
 
boss线程和worker线程所做的事情均分为3步。第一是轮询注册在Selector上的IO事件,第二是处理IO事件,第三是执行异步任务。对boss线程来说,第一步轮询出来的基本都是ACCEPT事件,表示有新的连接。对worker线程来说,第一步轮询出来的基本都是READ事件或WRITE事件,表示网络的读写。
 
(2)服务端启动流程
服务端是在用户线程中开启的,通过ServerBootstrap.bind()方法,在第一次添加异步任务的时候启动boss线程。启动之后,当前服务器就可以开启监听。
 
3.Netty新连接接入的整体处理逻辑
新连接接入的处理总体就是:检测新连接 + 注册Reactor线程,具体就可以分为如下4个过程。
 
一.检测新连接
服务端Channel对应的NioEventLoop会轮询该Channel绑定的Selector中是否发生了ACCEPT事件,如果是则说明有新连接接入了。
 
二.创建NioSocketChannel
检测出新连接之后,便会基于JDK NIO的Channel创建出一个NioSocketChannel,也就是客户端Channel。
 
三.分配worker线程及注册Selector
接着Netty给客户端Channel分配一个NioEventLoop,也就是分配worker线程。然后把这个客户端Channel注册到这个NioEventLoop对应的Selector上,之后这个客户端Channel的读写事件都会由这个NioEventLoop进行处理。
 
四.向Selector注册读事件
最后向这个客户端Channel对应的Selector注册READ事件,注册的逻辑和服务端Channel启动时注册ACCEPT事件的一样。
 
4.新连接接入之检测新连接
(1)何时会检测到有新连接
(2)新连接接入的流程梳理
(3)新连接接入的总结
 
(1)何时会检测到有新连接
当调用辅助启动类ServerBootstrap的bind()方法启动服务端之后,服务端的Channel也就是NioServerSocketChannel就会注册到boss的Reactor线程上。boss的Reactor线程会不断检测是否有新的事件,直到检测出有ACCEPT事件发生即有新连接接入。此时boss的Reactor线程将通过服务端Channel的unsafe变量来进行实际操作。
 
注意:服务端Channel的unsafe变量是一个NioMessageUnsafe对象,客户端Channel的unsafe变量是一个NioByteUnsafe对象。
  1. //SingleThreadEventLoop implementation which register the Channel's to a Selector and so does the multi-plexing of these in the event loop.
  2. public final class NioEventLoop extends SingleThreadEventLoop {
  3.     Selector selector;
  4.     private SelectedSelectionKeySet selectedKeys;
  5.     private boolean needsToSelectAgain;
  6.     private int cancelledKeys;
  7.     ...
  8.    
  9.     @Override
  10.     protected void run() {
  11.         for (;;) {
  12.             ...
  13.             //1.调用select()方法执行一次事件轮询
  14.             select(wakenUp.getAndSet(false));
  15.             if (wakenUp.get()) {
  16.                 selector.wakeup();
  17.             }
  18.             ...
  19.             //2.处理产生IO事件的Channel
  20.             needsToSelectAgain = false;
  21.             processSelectedKeys();
  22.             ...
  23.             //3.执行外部线程放入TaskQueue的任务
  24.             runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
  25.         }
  26.     }
  27.    
  28.     private void processSelectedKeys() {
  29.         if (selectedKeys != null) {
  30.             //selectedKeys.flip()会返回一个数组
  31.             processSelectedKeysOptimized(selectedKeys.flip());
  32.         } else {
  33.             processSelectedKeysPlain(selector.selectedKeys());
  34.         }
  35.     }
  36.    
  37.     private void processSelectedKeysOptimized(SelectionKey[] selectedKeys) {
  38.         for (int i = 0;; i ++) {
  39.             //1.首先取出IO事件
  40.             final SelectionKey k = selectedKeys[i];
  41.             if (k == null) {
  42.                 break;
  43.             }
  44.             selectedKeys[i] = null;//Help GC
  45.             //2.然后获取对应的Channel和处理该Channel
  46.             //默认情况下,这个a就是NioChannel,也就是服务端启动时经过Netty封装的Channel
  47.             final Object a = k.attachment();
  48.             if (a instanceof AbstractNioChannel) {
  49.                 //网络事件的处理
  50.                 processSelectedKey(k, (AbstractNioChannel) a);
  51.             } else {
  52.                 //NioTask主要用于当一个SelectableChannel注册到Selector时,执行的一些任务
  53.                 NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
  54.                 processSelectedKey(k, task);
  55.             }
  56.             //3.最后判断是否应该再进行一次轮询
  57.             if (needsToSelectAgain) {
  58.                 for (;;) {
  59.                     i++;
  60.                     if (selectedKeys[i] == null) {
  61.                         break;
  62.                     }
  63.                     selectedKeys[i] = null;
  64.                 }
  65.                 selectAgain();
  66.                 //selectedKeys.flip()会返回一个数组
  67.                 selectedKeys = this.selectedKeys.flip();
  68.                 i = -1;
  69.             }
  70.         }
  71.     }
  72.    
  73.     private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
  74.         final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
  75.         if (!k.isValid()) {
  76.             final EventLoop eventLoop;
  77.             try {
  78.                 eventLoop = ch.eventLoop();
  79.             } catch (Throwable ignored) {
  80.                 //If the channel implementation throws an exception because there is no event loop,
  81.                 //we ignore this because we are only trying to determine if ch is registered to this event loop and thus has authority to close ch.
  82.                 return;
  83.             }
  84.             //Only close ch if ch is still registerd to this EventLoop.
  85.             //ch could have deregistered from the event loop and thus the SelectionKey could be cancelled as part of the deregistration process,
  86.             //but the channel is still healthy and should not be closed.
  87.             if (eventLoop != this || eventLoop == null) {
  88.                 return;
  89.             }
  90.             //close the channel if the key is not valid anymore
  91.             unsafe.close(unsafe.voidPromise());
  92.             return;
  93.         }
  94.         try {
  95.             int readyOps = k.readyOps();
  96.             //We first need to call finishConnect() before try to trigger a read(...) or write(...) as otherwise
  97.             //the NIO JDK channel implementation may throw a NotYetConnectedException.
  98.             if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
  99.                 //remove OP_CONNECT as otherwise Selector.select(..) will always return without blocking
  100.                 int ops = k.interestOps();
  101.                 ops &= ~SelectionKey.OP_CONNECT;
  102.                 k.interestOps(ops);
  103.                 unsafe.finishConnect();
  104.             }
  105.             //Process OP_WRITE first as we may be able to write some queued buffers and so free memory.
  106.             if ((readyOps & SelectionKey.OP_WRITE) != 0) {
  107.                 //Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write
  108.                 ch.unsafe().forceFlush();
  109.             }
  110.             //Also check for readOps of 0 to workaround possible JDK bug which may otherwise lead to a spin loop
  111.             //boss的Reactor线程已经轮询到有ACCEPT事件,即表明有新连接接入
  112.             //此时将调用Channel的unsafe变量来进行实际操作
  113.             if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
  114.                 //进行新连接接入处理
  115.                 unsafe.read();
  116.                 if (!ch.isOpen()) {
  117.                     //Connection already closed - no need to handle write.
  118.                     return;
  119.                 }
  120.             }
  121.         } catch (CancelledKeyException ignored) {
  122.             unsafe.close(unsafe.voidPromise());
  123.         }
  124.     }
  125.     ...
  126. }
复制代码
(2)新连接接入的流程梳理
一.NioMessageUnsafe的read()方法说明
首先使用一条断言确保该read()方法必须来自Reactor线程调用,然后获得Channel对应的Pipeline和RecvByteBufAllocator.Handle。
 
接着调用NioServerSocketChannel的doReadMessages()方法不断地读取新连接到readBuf容器。然后使用for循环处理readBuf容器里的新连接,也就是通过pipeline.fireChannelRead()方法让每个新连接都经过一层服务端Channel的Pipeline逻辑处理,最后清理容器并执行pipeline.fireChannelReadComplete()。
  1. //AbstractNioChannel base class for Channels that operate on messages.
  2. public abstract class AbstractNioMessageChannel extends AbstractNioChannel {
  3.     ...
  4.     private final class NioMessageUnsafe extends AbstractNioUnsafe {
  5.         //临时存放读到的连接NioSocketChannel
  6.         private final List<Object> readBuf = new ArrayList<Object>();
  7.         @Override
  8.         public void read() {
  9.             //断言确保该read()方法必须来自Reactor线程调用
  10.             assert eventLoop().inEventLoop();
  11.             //获得Channel对应的Pipeline
  12.             final ChannelPipeline pipeline = pipeline();
  13.             //获得Channel对应的RecvByteBufAllocator.Handle
  14.             final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
  15.             do {
  16.                 //1.调用NioServerSocketChannel的doReadMessages()方法创建NioSocketChannel
  17.                 //通过JDK的accept()方法去创建JDK Channel,然后把它包装成Netty自定义的Channel
  18.                 int localRead = doReadMessages(readBuf);
  19.                 if (localRead == 0) {
  20.                     break;
  21.                 }
  22.             } while (allocHandle.continueReading());//控制连接的接入速率,默认一次性读取16个连接
  23.          
  24.             //2.设置并绑定NioSocketChannel
  25.             int size = readBuf.size();
  26.             for (int i = 0; i < size; i ++) {
  27.                 pipeline.fireChannelRead(readBuf.get(i));
  28.             }
  29.             //3.清理容器并触发pipeline.fireChannelReadComplete()
  30.             readBuf.clear();
  31.             pipeline.fireChannelReadComplete();
  32.         }
  33.     }
  34.    
  35.     //Read messages into the given array and return the amount which was read.
  36.     protected abstract int doReadMessages(List<Object> buf) throws Exception;
  37.     ...
  38. }
复制代码
二.新连接接入的流程梳理
首先会从服务端Channel对应的NioEventLoop的run()方法的第二个步骤处理IO事件开始。然后会调用服务端Channel的unsafe变量的read()方法,也就是NioMessageUnsafe对象的read()方法。
 
接着循环调用NioServerSocketChannel的doReadMessages()方法来创建新连接对象NioSocketChannel。其中创建新连接对象最核心的方法就是调用JDK Channel的accept()方法来创建JDK Channel。
 
与服务端启动一样,Netty会把JDK底层Channel包装成Netty自定义的NioSocketChannel。
  1. NioEventLoop.processSelectedKeys(key, channel) //入口
  2.   NioMessageUnsafe.read() //新连接接入处理
  3.     NioServerSocketChannel.doReadMessages() //创建新连接对象NioSocketChannel
  4.       javaChannel.accept() //创建JDK Channel
复制代码
(3)新连接接入的总结
在服务端Channel对应的NioEventLoop的run()方法的processSelectedKeys()方法里,发现产生的IO事件是ACCEPT事件之后,会通过JDK Channel的accept()方法取创建JDK的Channel,并把它包装成Netty自定义的NioSocketChannel。在这个过程中会通过一个RecvByteBufAllocator.Handle对象控制连接接入的速率,默认一次性读取16个连接。
 
5.新连接接入之创建NioSocketChannel
(1)doReadMessages()方法相关说明
(2)创建NioSocketChannel的流程梳理
(3)创建NioSocketChannel的总结
(4)Netty中的Channel分类
 
(1)doReadMessages()方法相关说明
首先通过javaChannel().accept()创建一个JDK的Channel,即客户端Channel。然后把服务端Channel和这个客户端Channel作为参数传入NioSocketChannel的构造方法中,从而把JDK的Channel封装成Netty自定义的NioSocketChannel。最后把封装好的NioSocketChannel添加到一个List里,以便外层可以遍历List进行处理。
  1. //A ServerSocketChannel implementation which uses NIO selector based implementation to accept new connections.
  2. public class NioServerSocketChannel extends AbstractNioMessageChannel implements ServerSocketChannel {
  3.     private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
  4.     private final ServerSocketChannelConfig config;
  5.     ...
  6.    
  7.     @Override
  8.     protected int doReadMessages(List<Object> buf) throws Exception {
  9.         //1.创建JDK的Channel
  10.         SocketChannel ch = javaChannel().accept();
  11.         //2.封装成Netty的Channel,即把服务端Channel和客户端Channel当作参数传递到NioSocketChannel的构造方法里
  12.         if (ch != null) {
  13.             //先创建一个NioSocketChannel对象,再添加到buf里
  14.             buf.add(new NioSocketChannel(this, ch));
  15.             return 1;
  16.         }
  17.         return 0;
  18.     }
  19.    
  20.     //Create a new instance
  21.     public NioServerSocketChannel() {
  22.         //创建服务端Channel
  23.         this(newSocket(DEFAULT_SELECTOR_PROVIDER));
  24.     }
  25.    
  26.     private static ServerSocketChannel newSocket(SelectorProvider provider) {
  27.         //创建服务端Channel
  28.         return provider.openServerSocketChannel();
  29.     }
  30.     //Create a new instance using the given ServerSocketChannel.
  31.     public NioServerSocketChannel(ServerSocketChannel channel) {
  32.         //创建服务端Channel,关注ACCEPT事件
  33.         super(null, channel, SelectionKey.OP_ACCEPT);
  34.         //javaChannel().socket()会调用JDK Channel的socket()方法
  35.         config = new NioServerSocketChannelConfig(this, javaChannel().socket());
  36.     }   
  37.      
  38.     @Override
  39.     protected ServerSocketChannel javaChannel() {
  40.         //返回一个JDK的Channel -> ServerSocketChannel
  41.         return (ServerSocketChannel) super.javaChannel();
  42.     }
  43.     ...
  44. }
  45. //AbstractNioChannel base class for Channels that operate on messages.
  46. public abstract class AbstractNioMessageChannel extends AbstractNioChannel {
  47.     ...
  48.     //创建服务端Channel
  49.     protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
  50.         super(parent, ch, readInterestOp);
  51.     }
  52.    
  53.     @Override
  54.     protected AbstractNioUnsafe newUnsafe() {
  55.         return new NioMessageUnsafe();
  56.     }
  57.     ...
  58. }
  59. //SocketChannel which uses NIO selector based implementation.
  60. public class NioSocketChannel extends AbstractNioByteChannel implements io.netty.channel.socket.SocketChannel {
  61.     private final SocketChannelConfig config;
  62.     ...
  63.    
  64.     //Create a new instance
  65.     //@param parent,the Channel which created this instance or null if it was created by the user
  66.     //@param socket,the SocketChannel which will be used
  67.     public NioSocketChannel(Channel parent, SocketChannel socket) {
  68.         //创建客户端Channel
  69.         super(parent, socket);
  70.         config = new NioSocketChannelConfig(this, socket.socket());
  71.     }
  72.    
  73.     @Override
  74.     protected SocketChannel javaChannel() {
  75.         //返回一个JDK的Channel -> ServerSocketChannel
  76.         return (SocketChannel) super.javaChannel();
  77.     }
  78.    
  79.     private final class NioSocketChannelConfig  extends DefaultSocketChannelConfig {
  80.         private NioSocketChannelConfig(NioSocketChannel channel, Socket javaSocket) {
  81.             super(channel, javaSocket);
  82.         }
  83.         ...
  84.     }
  85.     ...
  86. }
  87. //The default SocketChannelConfig implementation.
  88. public class DefaultSocketChannelConfig extends DefaultChannelConfig implements SocketChannelConfig {
  89.     protected final Socket javaSocket;
  90.   
  91.     //Creates a new instance.
  92.     public DefaultSocketChannelConfig(SocketChannel channel, Socket javaSocket) {
  93.         ...
  94.         this.javaSocket = javaSocket;
  95.         setTcpNoDelay(true);//禁止Nagle算法
  96.         ...
  97.     }
  98.     ...
  99. }
  100. //AbstractNioChannel base class for Channels that operate on bytes.
  101. public abstract class AbstractNioByteChannel extends AbstractNioChannel {
  102.     ...
  103.     //Create a new instance
  104.     //@param parent,the parent Channel by which this instance was created. May be null
  105.     //@param ch,the underlying SelectableChannel on which it operates
  106.     protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
  107.         //创建客户端Channel,关注READ事件
  108.         super(parent, ch, SelectionKey.OP_READ);
  109.     }
  110.    
  111.     @Override
  112.     protected AbstractNioUnsafe newUnsafe() {
  113.         return new NioByteUnsafe();
  114.     }
  115.     ...
  116. }
  117. //Abstract base class for Channel implementations which use a Selector based approach.
  118. public abstract class AbstractNioChannel extends AbstractChannel {
  119.     private final SelectableChannel ch;
  120.     protected final int readInterestOp;
  121.     ...
  122.     //Create a new instance
  123.     //@param parent,the parent Channel by which this instance was created. May be null
  124.     //@param ch,the underlying SelectableChannel on which it operates
  125.     //@param readInterestOp,the ops to set to receive data from the SelectableChannel
  126.     protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
  127.         super(parent);
  128.         this.ch = ch;
  129.         this.readInterestOp = readInterestOp;
  130.         ch.configureBlocking(false);
  131.         ...
  132.     }
  133.    
  134.     protected SelectableChannel javaChannel() {
  135.         return ch;
  136.     }
  137.    
  138.     @Override
  139.     public NioUnsafe unsafe() {
  140.         return (NioUnsafe) super.unsafe();
  141.     }
  142.     ...
  143. }
  144. //A skeletal Channel implementation.
  145. public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
  146.     private final Channel parent;
  147.     private final ChannelId id;
  148.     private final Unsafe unsafe;
  149.     private final DefaultChannelPipeline pipeline;
  150.     ...
  151.    
  152.     //Creates a new instance.
  153.     //@param parent,the parent of this channel. null if there's no parent.
  154.     protected AbstractChannel(Channel parent) {
  155.         this.parent = parent;
  156.         id = newId();
  157.         unsafe = newUnsafe();
  158.         pipeline = newChannelPipeline();
  159.     }
  160.    
  161.     //Returns a new DefaultChannelId instance.
  162.     //Subclasses may override this method to assign custom ChannelIds to Channels that use the AbstractChannel#AbstractChannel(Channel) constructor.
  163.     protected ChannelId newId() {
  164.         return DefaultChannelId.newInstance();
  165.     }
  166.    
  167.     //Create a new AbstractUnsafe instance which will be used for the life-time of the Channel
  168.     protected abstract AbstractUnsafe newUnsafe();
  169.    
  170.     //Returns a new DefaultChannelPipeline instance.
  171.     protected DefaultChannelPipeline newChannelPipeline() {
  172.         return new DefaultChannelPipeline(this);
  173.     }
  174.     @Override
  175.     public Unsafe unsafe() {
  176.         return unsafe;
  177.     }
  178.    
  179.     @Override
  180.     public ChannelPipeline pipeline() {
  181.         return pipeline;
  182.     }
  183.    
  184.     @Override
  185.     public EventLoop eventLoop() {
  186.         EventLoop eventLoop = this.eventLoop;
  187.         if (eventLoop == null) throw new IllegalStateException("channel not registered to an event loop");
  188.         return eventLoop;
  189.     }
  190.    
  191.     protected abstract class AbstractUnsafe implements Unsafe {
  192.         @Override
  193.         public final void register(EventLoop eventLoop, final ChannelPromise promise) {
  194.             ...
  195.             //绑定事件循环器,即绑定一个NioEventLoop到该Channel上
  196.             AbstractChannel.this.eventLoop = eventLoop;
  197.             //注册Selector,并启动一个NioEventLoop
  198.             if (eventLoop.inEventLoop()) {
  199.                 register0(promise);
  200.             } else {
  201.                 ...
  202.                 //通过启动这个NioEventLoop线程来调用register0()方法将这个服务端Channel注册到Selector上
  203.                 //其实执行的是SingleThreadEventExecutor的execute()方法
  204.                 eventLoop.execute(new Runnable() {
  205.                     @Override
  206.                     public void run() {
  207.                         register0(promise);
  208.                     }
  209.                 });
  210.                 ...
  211.             }
  212.         }
  213.         ...
  214.     }
  215.     ...
  216. }
复制代码
(2)创建NioSocketChannel的流程梳理
NioServerSocketChannel和NioSocketChannel都有同一个父类AbstractNioChannel,所以创建NioSocketChannel的模版和创建NioServerSocketChannel保持一致。
 
但要注意的是:客户端Channel是通过new关键字创建的,服务端Channel是通过反射的方式创建的。
 
此外,Nagle算法会让小数据包尽量聚合成大的数据包再发送出去,Netty为了使数据能够及时发送出去会禁止该算法。
  1. new NioSocketChannel(p, ch) //入口,客户端Channel是通过new关键字创建的,服务端Channel是通过反射的方式创建的
  2.   new AbstractNioByteChannel(p, ch) //逐层调用父类的构造方法
  3.     new AbstractNioChannel(p, ch, op_read) //逐层调用父类的构造方法
  4.       ch.configureBlocking(false) + save op //配置此Channel为非阻塞,以及将感兴趣的读事件保存到成员变量以方便后续注册到Selector
  5.       new AbstractChannel() //创建Channel的相关组件:
  6.         newId() //id作为Channel的唯一标识
  7.         newUnsafe() //unsafe用来进行底层数据读写
  8.         newChannelPipeline() //pipeline作为业务逻辑载体
  9.   new NioSocketChannelConfig() //创建和NioSocketChannel绑定的配置类
  10.     setTcpNoDelay(true) //禁止Nagle算法
复制代码
(3)创建NioSocketChannel的总结
创建NioSocketChannel的逻辑可以分成两部分。
 
第一部分是逐层调用父类的构造方法,其中会设置这个客户端Channel的阻塞模式为false,然后再把感兴趣的读事件OP_READ保存到这个Channel的成员变量中以便后续注册到Selector,接着会创建一系列的组件,包括作为Channel唯一标识的Id组件、用来进行底层数据读写的unsafe组件、用来作为业务逻辑载体的pipeline组件。
 
第二部分是创建和这个客户端Channel相关的config对象,该config对象会设置关闭Nagle算法,从而让小数据包尽快发送出去、降低延时。
 
(4)Netty中的Channel分类
1.png
说明一:
Channel继承Comparable表示Channel是一个可以比较的对象。
 
说明二:
Channel继承AttributeMap表示Channel是一个可以绑定属性的对象,我们经常在代码中使用channel.attr(...)来给Channel绑定属性,其实就是把属性设置到AttributeMap中。
 
说明三:
AbstractChannel用来实现Channel的大部分方法,在AbstractChannel的构造方法中会创建一个Channel对象所包含的基本组件,这里的Channel通常是指SocketChannel和ServerSocketChannel。
 
说明四:
AbstractNioChannel继承了AbstractChannel,然后通过Selector处理一些NIO相关的操作。比如它会保存JDK底层SelectableChannel的引用,并且在构造方法中设置Channel为非阻塞模式。注意:设置非阻塞模式是NIO编程必须的。
 
说明五:
Netty的两大Channel是指:服务端的NioServerSocketChannel和客户端NioSocketChannel,分别对应着服务端接收新连接的过程和服务端新连接读写数据的过程。
 
说明六:
服务端Channel和客户端Channel的区别是:服务端Channel通过反射方式创建,客户端Channel通过new关键字创建。服务端Channel注册的是ACCEPT事件,对应接收新连接。客户端Channel注册的是READ事件,对应新连接读写。服务端Channel和客户端Channel底层都会依赖一个unsafe对象,这个unsafe对象会用来实现这两种Channel底层的数据读写操作。对于读操作,服务端的读是读一条连接doReadMessages(),客户端的读是读取数据doReadBytes()。最后每一个Channel都会绑定一个ChannelConfig,每一个ChannelConfig都会实现Channel的一些配置。
 
6.新连接接入之绑定NioEventLoop线程
(1)将新连接绑定到Reactor线程的入口
(2)服务端Channel的Pipeline介绍
(3)服务端Channel默认的Pipeline处理器
(4)服务端Channel处理新连接的步骤
(5)总结
 
(1)将新连接绑定到Reactor线程的入口
创建完NioSocketChannel后,接下来便要对NioSocketChannel进行一些设置,并且需要将它绑定到一个正在执行的Reactor线程中。
 
NioMessageUnsafe.read()方法里的readBuf容器会承载着所有新建的连接,如果某个时刻Netty轮询到多个连接,那么通过使用for循环就可以批量处理这些NioSocketChannel连接。
 
处理每个NioSocketChannel连接时,是通过NioServerSocketChannel的pipeline的fireChannelRead()方法来处理的。
  1. //SingleThreadEventLoop implementation which register the Channel's to a Selector and so does the multi-plexing of these in the event loop.
  2. public final class NioEventLoop extends SingleThreadEventLoop {
  3.     Selector selector;
  4.     private SelectedSelectionKeySet selectedKeys;
  5.     private boolean needsToSelectAgain;
  6.     private int cancelledKeys;
  7.     ...
  8.    
  9.     @Override
  10.     protected void run() {
  11.         for (;;) {
  12.             ...
  13.             //1.调用select()方法执行一次事件轮询
  14.             select(wakenUp.getAndSet(false));
  15.             if (wakenUp.get()) {
  16.                 selector.wakeup();
  17.             }
  18.             ...
  19.             //2.处理产生IO事件的Channel
  20.             needsToSelectAgain = false;
  21.             processSelectedKeys();
  22.             ...
  23.             //3.执行外部线程放入TaskQueue的任务
  24.             runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
  25.         }
  26.     }
  27.    
  28.     private void processSelectedKeys() {
  29.         if (selectedKeys != null) {
  30.             //selectedKeys.flip()会返回一个数组
  31.             processSelectedKeysOptimized(selectedKeys.flip());
  32.         } else {
  33.             processSelectedKeysPlain(selector.selectedKeys());
  34.         }
  35.     }
  36.    
  37.     private void processSelectedKeysOptimized(SelectionKey[] selectedKeys) {
  38.         for (int i = 0;; i ++) {
  39.             //1.首先取出IO事件
  40.             final SelectionKey k = selectedKeys[i];
  41.             if (k == null) {
  42.                 break;
  43.             }
  44.             selectedKeys[i] = null;//Help GC
  45.             //2.然后获取对应的Channel和处理该Channel
  46.             //默认情况下,这个a就是NioChannel,也就是服务端启动时经过Netty封装的Channel
  47.             final Object a = k.attachment();
  48.             if (a instanceof AbstractNioChannel) {
  49.                 //网络事件的处理
  50.                 processSelectedKey(k, (AbstractNioChannel) a);
  51.             } else {
  52.                 //NioTask主要用于当一个SelectableChannel注册到Selector时,执行的一些任务
  53.                 NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
  54.                 processSelectedKey(k, task);
  55.             }
  56.             //3.最后判断是否应该再进行一次轮询
  57.             if (needsToSelectAgain) {
  58.                 for (;;) {
  59.                     i++;
  60.                     if (selectedKeys[i] == null) {
  61.                         break;
  62.                     }
  63.                     selectedKeys[i] = null;
  64.                 }
  65.                 selectAgain();
  66.                 //selectedKeys.flip()会返回一个数组
  67.                 selectedKeys = this.selectedKeys.flip();
  68.                 i = -1;
  69.             }
  70.         }
  71.     }
  72.    
  73.     private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
  74.         final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
  75.         ...
  76.         try {
  77.             int readyOps = k.readyOps();
  78.             //We first need to call finishConnect() before try to trigger a read(...) or write(...) as otherwise
  79.             //the NIO JDK channel implementation may throw a NotYetConnectedException.
  80.             if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
  81.                 //remove OP_CONNECT as otherwise Selector.select(..) will always return without blocking
  82.                 int ops = k.interestOps();
  83.                 ops &= ~SelectionKey.OP_CONNECT;
  84.                 k.interestOps(ops);
  85.                 unsafe.finishConnect();
  86.             }
  87.             //Process OP_WRITE first as we may be able to write some queued buffers and so free memory.
  88.             if ((readyOps & SelectionKey.OP_WRITE) != 0) {
  89.                 //Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write
  90.                 ch.unsafe().forceFlush();
  91.             }
  92.             //Also check for readOps of 0 to workaround possible JDK bug which may otherwise lead to a spin loop
  93.             //boss的Reactor线程已经轮询到有ACCEPT事件,即表明有新连接接入
  94.             //此时将调用Channel的unsafe变量来进行实际操作
  95.             if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
  96.                 //进行新连接接入处理
  97.                 unsafe.read();
  98.                 if (!ch.isOpen()) {
  99.                     //Connection already closed - no need to handle write.
  100.                     return;
  101.                 }
  102.             }
  103.         } catch (CancelledKeyException ignored) {
  104.             unsafe.close(unsafe.voidPromise());
  105.         }
  106.     }
  107.     ...
  108. }
  109. //AbstractNioChannel base class for Channels that operate on messages.
  110. public abstract class AbstractNioMessageChannel extends AbstractNioChannel {
  111.     ...
  112.     private final class NioMessageUnsafe extends AbstractNioUnsafe {
  113.         //临时存放读到的连接NioSocketChannel
  114.         private final List<Object> readBuf = new ArrayList<Object>();
  115.         @Override
  116.         public void read() {
  117.             //断言确保该read()方法必须来自Reactor线程调用
  118.             assert eventLoop().inEventLoop();
  119.             //获得Channel对应的Pipeline
  120.             final ChannelPipeline pipeline = pipeline();
  121.             //获得Channel对应的RecvByteBufAllocator.Handle
  122.             final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
  123.             do {
  124.                 //1.调用NioServerSocketChannel的doReadMessages()方法创建NioSocketChannel
  125.                 //通过JDK的accept()方法去创建JDK Channel,然后把它包装成Netty自定义的Channel
  126.                 int localRead = doReadMessages(readBuf);
  127.                 if (localRead == 0) {
  128.                     break;
  129.                 }
  130.             } while (allocHandle.continueReading());//控制连接的接入速率,默认一次性读取16个连接
  131.          
  132.             //2.设置并绑定NioSocketChannel
  133.             int size = readBuf.size();
  134.             for (int i = 0; i < size; i ++) {
  135.                 //调用DefaultChannelPipeline的fireChannelRead()方法
  136.                 //开始处理每个NioSocketChannel连接
  137.                 pipeline.fireChannelRead(readBuf.get(i));
  138.             }
  139.             //3.清理容器并触发DefaultChannelPipeline的fireChannelReadComplete()方法
  140.             readBuf.clear();
  141.             //结束处理每个NioSocketChannel连接
  142.             pipeline.fireChannelReadComplete();
  143.         }
  144.     }
  145.    
  146.     //Read messages into the given array and return the amount which was read.
  147.     protected abstract int doReadMessages(List<Object> buf) throws Exception;
  148.     ...
  149. }
复制代码
(2)服务端Channel的Pipeline介绍
在Netty的各种类型的Channel中,都会包含一个Pipeline。Pipeline可理解为一条流水线,流水线有起点有结束,中间还会有各种各样的流水线关卡。对Channel的处理会在流水线的起点开始,然后经过各个流水线关卡的加工,最后到达流水线的终点结束。
 
流水线Pipeline的开始是HeadContext,结束是TailContext。HeadContext中会调用Unsafe进行具体的操作,TailContext中会向用户抛出流水线Pipeline中未处理异常和未处理消息的警告。
 
在服务端的启动过程中,Netty会给服务端Channel自动添加一个Pipeline处理器ServerBootstrapAcceptor,并且会将用户代码中设置的一系列参数传入到这个ServerBootstrapAcceptor的构造方法中。
 
服务端Channel的Pipeline如下所示:
2.png
所以服务端Channel的Pipeline在传播ChannelRead事件时首先会从HeadContext处理器开始,然后传播到ServerBootstrapAcceptor处理器,最后传播到TailContext处理器结束。
 
(3)服务端Channel默认的Pipeline处理器
首先,服务端启动时会给服务端Channel的Pipeline添加一个ServerBootstrapAcceptor处理器。
  1. //Bootstrap sub-class which allows easy bootstrap of ServerChannel
  2. public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {
  3.     ...
  4.     @Override
  5.     void init(Channel channel) throws Exception {
  6.         //1.设置服务端Channel的Option与Attr
  7.         final Map<ChannelOption<?>, Object> options = options0();
  8.         synchronized (options) {
  9.             channel.config().setOptions(options);
  10.         }
  11.         final Map, Object> attrs = attrs0();
  12.         synchronized (attrs) {
  13.             for (Entry, Object> e: attrs.entrySet()) {
  14.                 @SuppressWarnings("unchecked")
  15.                 AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
  16.                 channel.attr(key).set(e.getValue());
  17.             }
  18.         }
  19.       
  20.         //2.设置客户端Channel的Option与Attr
  21.         final EventLoopGroup currentChildGroup = childGroup;
  22.         final ChannelHandler currentChildHandler = childHandler;
  23.         final Entry<ChannelOption<?>, Object>[] currentChildOptions;
  24.         final Entry, Object>[] currentChildAttrs;
  25.         synchronized (childOptions) {
  26.             currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));
  27.         }
  28.         synchronized (childAttrs) {
  29.             currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));
  30.         }
  31.       
  32.         //3.配置服务端启动逻辑
  33.         ChannelPipeline p = channel.pipeline();
  34.         //p.addLast()用于定义服务端启动过程中需要执行哪些逻辑
  35.         p.addLast(new ChannelInitializer<Channel>() {
  36.             @Override
  37.             public void initChannel(Channel ch) throws Exception {
  38.                 //一.添加用户自定义的Handler,注意这是handler,而不是childHandler
  39.                 final ChannelPipeline pipeline = ch.pipeline();
  40.                 ChannelHandler handler = config.handler();
  41.                 if (handler != null) pipeline.addLast(handler);
  42.                 //二.添加一个特殊的Handler用于接收新连接
  43.                 //自定义的childHandler会作为参数传入连接器ServerBootstrapAcceptor
  44.                 ch.eventLoop().execute(new Runnable() {
  45.                     @Override
  46.                     public void run() {
  47.                         //调用DefaultChannelPipeline的addLast()方法
  48.                         pipeline.addLast(new ServerBootstrapAcceptor(
  49.                             currentChildGroup,
  50.                             currentChildHandler,
  51.                             currentChildOptions,
  52.                             currentChildAttrs)
  53.                         );
  54.                     }
  55.                 });
  56.             }
  57.         });
  58.     }
  59.     ...
  60. }
  61. //A skeletal Channel implementation.
  62. public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
  63.     private final DefaultChannelPipeline pipeline;
  64.     ...
  65.     //Creates a new instance.
  66.     protected AbstractChannel(Channel parent) {
  67.         this.parent = parent;
  68.         id = newId();
  69.         unsafe = newUnsafe();
  70.         pipeline = newChannelPipeline();
  71.     }
  72.    
  73.     protected DefaultChannelPipeline newChannelPipeline() {
  74.         return new DefaultChannelPipeline(this);
  75.     }
  76.     @Override
  77.     public ChannelPipeline pipeline() {
  78.         return pipeline;
  79.     }
  80.     ...
  81. }
  82. //The default ChannelPipeline implementation.  
  83. //It is usually created by a Channel implementation when the Channel is created.
  84. public class DefaultChannelPipeline implements ChannelPipeline {
  85.     final AbstractChannelHandlerContext head;
  86.     final AbstractChannelHandlerContext tail;
  87.     ...
  88.     protected DefaultChannelPipeline(Channel channel) {
  89.         ...
  90.         tail = new TailContext(this);
  91.         head = new HeadContext(this);
  92.         head.next = tail;
  93.         tail.prev = head;
  94.     }
  95.    
  96.     @Override
  97.     public final ChannelPipeline addLast(ChannelHandler... handlers) {
  98.         return addLast(null, handlers);
  99.     }
  100.     @Override
  101.     public final ChannelPipeline addLast(EventExecutorGroup executor, ChannelHandler... handlers) {
  102.         ...
  103.         for (ChannelHandler h: handlers) {
  104.             if (h == null) break;
  105.             addLast(executor, null, h);
  106.         }
  107.         return this;
  108.     }
  109.    
  110.     @Override
  111.     public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
  112.         final AbstractChannelHandlerContext newCtx;
  113.         synchronized (this) {
  114.             checkMultiplicity(handler);
  115.             newCtx = newContext(group, filterName(name, handler), handler);
  116.             addLast0(newCtx);
  117.             ...
  118.         }
  119.         ...
  120.     }
  121.    
  122.     //往Pipeline中添加ChannelHandler处理器
  123.     private void addLast0(AbstractChannelHandlerContext newCtx) {
  124.         AbstractChannelHandlerContext prev = tail.prev;
  125.         newCtx.prev = prev;
  126.         newCtx.next = tail;
  127.         prev.next = newCtx;
  128.         tail.prev = newCtx;
  129.     }
  130.     ...
  131. }
复制代码
然后,新连接接入调用到服务端Channel的Pipeline的fireChannelRead()方法时,便会触发调用ServerBootstrapAcceptor处理器的channelRead()方法。最终会调用NioEventLoop的register()方法注册这个新连接Channel,即给新连接Channel绑定一个Reactor线程。
[code]//The default ChannelPipeline implementation.  //It is usually created by a Channel implementation when the Channel is created.public class DefaultChannelPipeline implements ChannelPipeline {    final AbstractChannelHandlerContext head;    final AbstractChannelHandlerContext tail;    ...        protected DefaultChannelPipeline(Channel channel) {        ...        tail = new TailContext(this);        head = new HeadContext(this);        head.next = tail;        tail.prev = head;    }        @Override    public final ChannelPipeline fireChannelRead(Object msg) {        //从Pipeline的第一个HeadContext处理器开始调用        AbstractChannelHandlerContext.invokeChannelRead(head, msg);        return this;    }        final class HeadContext extends AbstractChannelHandlerContext implements ChannelOutboundHandler, ChannelInboundHandler {        ...        @Override        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {            //调用AbstractChannelHandlerContext的fireChannelRead()方法            ctx.fireChannelRead(msg);        }             @Override        public ChannelHandler handler() {            return this;        }        ...    }    ...}abstract class AbstractChannelHandlerContext extends DefaultAttributeMap implements ChannelHandlerContext, ResourceLeakHint {    ...    static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {        final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);        EventExecutor executor = next.executor();        if (executor.inEventLoop()) {            next.invokeChannelRead(m);        } else {            executor.execute(new Runnable() {                @Override                public void run() {                    next.invokeChannelRead(m);                }            });        }    }    private void invokeChannelRead(Object msg) {        if (invokeHandler()) {            try {                //首先调用的是Pipeline的第一个处理器HeadContext的channelRead()方法                //注意:HeadContext继承了AbstractChannelHandlerContext                ((ChannelInboundHandler) handler()).channelRead(this, msg);            } catch (Throwable t) {                notifyHandlerException(t);            }        } else {            fireChannelRead(msg);        }    }        @Override    public ChannelHandlerContext fireChannelRead(final Object msg) {        invokeChannelRead(findContextInbound(), msg);        return this;    }        private AbstractChannelHandlerContext findContextInbound() {        AbstractChannelHandlerContext ctx = this;        do {            //注意:HeadContext继承了AbstractChannelHandlerContext            //所以如果this是HeadContext,那么这里会获取下一个节点ServerBootstrapAcceptor            ctx = ctx.next;        } while (!ctx.inbound);        return ctx;    }    ...}public class ServerBootstrap extends AbstractBootstrap {    ...    private static class ServerBootstrapAcceptor extends ChannelInboundHandlerAdapter {        private final EventLoopGroup childGroup;        private final ChannelHandler childHandler;        private final Entry
您需要登录后才可以回帖 登录 | 立即注册