Netty 概念与名词

系列前言 Netty 作为使用最广泛的网络IO通信框架, 每一个合格的 Java 后台的开发者都应该对其了如指掌. 但是由于 Netty 项目涉及到比较多的版本迭代, API 也比较丰富. 源码在阅读上相对有一定难度, 所以本系列文章也无法确定更新计划与文章深度, 只能如同拾人牙慧, 外加自己一些浅显的见解, 拼凑出一个仅供自己复习的知识框架

本篇前言与其他的技术不同, 由于 Netty 的应用实在过于普遍又十分重要, 例如阿里广泛使用的 hsf 与 RocketMQ. 所以本系列文章会从如何使用开始, 接下来涉及到源码原理部分, 最后总结其特点与应用位置.

概念与名词

在上一篇文章中, 在 Java NIO 中引入了 Channel/Selector/Select 等概念, 在 Netty 中还需要了解以下概念

  1. Reactor

Reactor 并不是 Netty 特有的概念, 而是相对于 BIO 的 Acceptor 的概念提出的, Reactor 翻译叫反应堆, 其实更应该叫 Notifier/Dispatcher. 由于计算机中 CPU 的速度远大于 IO 的速度, 可以认为 CPU 比 IO 繁忙许多, 所以让 CPU 等待 IO 是不现实的.

在NIO 中采用回调(event handler)的方式, 完成这个事情. 应用业务通过 Reactor 注册一个 event handler, 每当 IO 就绪, Reactor 产生一个事件, 会通知 event handler 进行处理.

Reactor 可以看作一个大老板的秘书. 大老板有很多繁忙工作要做, 但是手下的 IO 每次都会执行很长, 于是每当大老板需要执行 IO 时, 他通过他的秘书 Reactor 通过event handler 征召手下执行 IO 任务. 这也就是所谓的 “Hollywood principle”: “Don’t call us, we’ll call you.”

在上面的例子中, 一个 Reactor 不断的等待循环, 一方面接受 event handler 注册, 另一方面查询 IO 是否就绪, 并且在就绪后制定对应的 event handler 执行 IO

2.Procator

Reactor 中, 操作系统只负责通知IO就绪, 具体的IO还是由对应的线程产生阻塞, Procator 则是由操作系统完成 IO, handler 只处理自己的逻辑, Procator 是一种异步IO. 用的会稍微比较少, 在 netty 中一般使用同步 io + future 的方式.

特殊补充: IO方式的辨析

AIO/BIO/NIO 以及其他几种 IO 如何辨析呢, 我一直喜欢用例子的方式来比喻. 假设两个人需要通过电话保持联系

i. BIO的方式属于把电话接通, 一直开着, 无论有没有事情在此期间随时保持着监听的状态, 不能干其他的事情

ii. NIO的方式属于电话处于待机状态, 如果有事情就拨通号码, 此时另一方电话会处于响铃状态, 另一方会一边工作一边没事检查手机, 检查手机时看到拨号过来后于是会接通电话.

iii. Channel/Selector模式属于一个人拿着好几个电话, 等在边上盯着这几个电话(注意此时是阻塞状态), 哪一个电话响了就拿起哪一个手机开始通话.

iv. Futrue 模式, 把电话搁在一边, 先去做自己的工作, 工作做完了再去等着电话响铃, 可以一直等也可以设置等待一个时常

v. 添加监听器的Future 模式, 电话搁在一边去做自己的工作, 电话如果响铃了就让它响着, 响完了之后会自动启动我设置的监听器, 将结果处理/保存/展示/丢弃等等, 我之后会调用相关方法进行处理, 这其实是一种异步方式, 这也是 Netty 强烈推荐使用的一种方式.

vi. 纯异步模式, 相当于语音信箱或者微信语音.

  1. Bootstrap

 

Bootstrap是netty 的启动器, 起到了入口的作用. Bootstrap 类一般用于创建客户端连接, ServerBootstrap 是服务端的监听端口启动器. Bootstrap 与 ServerBootstrap 都继承了 AbstractBootstrap 这个类, AbstractBootstrap 在后面还会详细介绍. 总的来说, Bootstrap 与 Tomcat 的 Bootstrap 相类似, 都是作为启动器启动应用

  1. Channel

Channel 提供了与原生 jdk socket 相关联的组件, 例如 NioServerSocketChannel 和 NioSocketChannel, NioServerSocketChannel 通过监听一个 tcp 端口, 有连接进来后通过 boss reactor 创建一个 NioSocketChannel 将其绑定到 worker reactor, 然后由这个worker reactor 负责 NioSocketChannel 的读写等 IO 事件.

  1. EventLoop

即 reactor, netty 中 reactor 被分为 boss reactor 和 worker reactor, 默认构造方法是 EventLoopGroup. 在 NioEventLoop 中, 存在一个无限循环的 run() 方法, 根据不同的 SelectStrategy, 一直轮询注册的 io 事件, 一个 NioEventLoop 维护了多条连接.

  1. ChannelPipeline

ChannelPipeline 实际上是 ChannelHandler 的容器, netty 处理 io 即是通过 pipeline 中的 ChannelHandler 的过程. 我们可以通过 SocketChannel.pipeline() 获取其 pipeline, 通过 addLast()/addFirst() 添加 ChannelHandler. ChannelHandler 和 ChannelPipeline 组成责任链, 使得添加的 ChannelHandler 以管道的方式执行. ChannelPipeline 内部有两个节点, head 和 tail, 分别对应着 ChannelHandler 链的头和尾.

  1. ChannelHandler

netty 处理 io 事件的处理单元, 开发者创建自己的 ChannelHandler 并重写对应的方法来处理自己的 io 逻辑. ChannelHandler 分为 inBound 和 outBound, 对应 io 的 read 和 write 管道. ChannelHandler 用 ChannelHandlerContext 包裹着, 有 prev 和 next 节点, read时从 ChannelPipeline 的 head 执行到 tail, write 时从 tail 执行到 head, 所以 head 既是 read 事件的起点也是 write 事件的终点,与 io 交互最紧密.

8.Unsafe

这个类本身并不是不安全, 而是不要在应用程序里面直接使用 Unsafe 以及他的衍生类对象(在介绍Reentrant Lock 时 sun 下面也有这个同名的包, 其含义相同). 实际上 Unsafe 操作都是在 reactor 线程中被执行. Unsafe 是 Channel 的内部类, 并且是 protected 修饰的, 所以在类的设计上已经保证了不被用户代码调用. Unsafe 的操作都是和 jdk 底层相关. EventLoop 轮询到 read 或 accept 事件时, 会调用 unsafe.read(), unsafe再调用ChannelPipeline去处理事件; 当发生write事件时, 所有写事件都会放在EventLoop的task中, 然后从 ChannelPipeline 的 tail 传播到 head, 通过Unsafe写到网络中.
系列前言 Netty 作为使用最广泛的网络IO通信框架, 每一个合格的 Java 后台的开发者都应该对其了如指掌. 但是由于 Netty 项目涉及到比较多的版本迭代, API 也比较丰富. 源码在阅读上相对有一定难度, 所以本系列文章也无法确定更新计划与文章深度, 只能如同拾人牙慧, 外加自己一些浅显的见解, 拼凑出一个仅供自己复习的知识框架

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.