永动机即'永远运动的机器'.
作为服务端时, 需要接收客户端的连接, 需要接收客户端的数据, 或者向客户端发送数据.
既然需要接收客户端的连接, 那么就需要一个IO线程永远的执行一个无限循环.只有一直循环着, 才能一直接收新的连接.
NioEventLoop的底层绑定一个线程, 这个线程在启动之后, 就会一直无限循环着, 而且只做三件事
1.轮询IO事件
2.处理IO事件
3.执行任务
当有新的客户端连接到服务端的时候(TCP三次握手已经完成), 服务端的IO线程就会轮询到有客户端的连接****事件. 接下来就会处理这个连接事件.
IO线程会创建一个针对这个客户端与之对应的NioSocketChannel, 然后把这个Channel注册到另一类NioEventLoop(它的底层也会绑定一个线程)上.
从图中可以看出, 右侧的IO线程只会负责读写事件, 并不会负责连接事件.
当客户端给服务端发送数据的时候, 服务端IO线程轮询到读事件, 接下来就会处理这个读事件. 会读取到客户端发送过来的数据, 经过解码器解码, 再把数据传给业务Handler进行处理.
然而一般情况下, 当服务端需要向客户端写数据的时候, 直接调用相应的函数(writeAndFlush)即可, 并不会涉及到写事件. 那么什么时候才会涉及到写事件呢? 当网络出现拥堵的情况, 或者客户端没有及时处理服务端发给它的数据. 那么服务端的Socket的TCP缓冲区就会被写满, 这个时候再向缓冲区写数据就会失败, Netty就会注册一个写事件. 当TCP缓冲区可写的时候, Netty就会继续将之前没有写完的数据,再次向TCP缓冲区写.
如果把Netty的服务端比作一台大型的机器. 那么在这个机器里, 有好几个一直运作地大齿轮(每个NioEventLoop就是一个大齿轮). 这些大齿轮一直转着,一直转着, 从不停止.
以上说了服务端的三个IO事件, 分别是连接事件, 读事件, 写事件. 准确的说应该是接收连接事件, 读事件, 写事件.
因为在客户端也有三个IO事件, 分别是连接事件, 读事件, 写事件. 如下图
如上图右侧所示, 客户端轮询着连接事件, 读事件和写事件. 读写事件和服务端一样, 说一下连接事件.
Netty在进行TCP三次握手的时候, 由于网络等原因, Netty并没有一直等待着连接完成, 客户端在发起连接之后, 便注册了一个连接事件. 当TCP三次握手完成之后, IO线程轮询到了连接完成事件. 接下来就会做一些连接完成的后续工作, 同时取消连接事件. 关键代码如下
// 源码位置: io.netty.channel.socket.nio.NioSocketChannel#doConnect
// 源码位置: io.netty.channel.nio.NioEventLoop#processSelectedKey(java.nio.channels.SelectionKey, io.netty.channel.nio.AbstractNioChannel)
总结: 此篇文件简单说了下Netty作为服务端和客户端的时候, IO线程一直在无限循环着, 傻傻地做着轮询IO事件, 处理IO事件, 执行任务这三件事.
本文分享自微信公众号 - Netty历险记(infuq217)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。