前言
上篇文章《听说96.5%的程序员都没用过Netty》主要涵盖了 Netty 的入门知识,包括 Netty 的发展历程、核心功能与组件,并且通过实例演示了如何使用 Netty 构建一个 HTTP 服务器。由于 Netty 的抽象程度较高,因此理解起来可能会比较复杂,所以本文通过对比 Java NIO API 和 Netty 的示例代码,并结合Netty源码进行深入剖析,从而更好的理解Netty的工作原理。
Java NIO 网络编程流程
首先,我们知道 Netty 是基于 Java NIO API 封装扩展的一个网络编程框架,所以二者的网络编程工作流程是大差不差的。
(所以在深入了解Netty之前,建议先对Java NIO有一定的了解)
下图是基于Java NIO API进行网络编程时的工作流程。涉及到的Selector、Channel、Buffer这几个组件,《Java NIO是New IO还是Non-blocking IO》中有详细说明。
Java NIO 工作原理
Netty 编码 VS Java NIO API 编码
Netty 同样会用到这些核心组件,只不过再一次抽象封装后,不能直观的看到这些组件。
先看下基于Java NIO API编程是怎么编写代码的。
Java NIO代码示例
可以看到, ServerSocketChannel 注册到 Selector 并监听连接事件,当 Selector 接收到连接的客户端 Channel 后,又监听 Channel 的读写事件,那么下次 Selector 就会接收到客户端的读写事件并进行处理。
再看一下基于Netty构建的HTTP服务器示例。
Netty 编程
能看到的就只有一个对客户端读请求处理的 channelRead0 函数,Selector、ServerSocketChannel这些都没看到。
那么 ServerSocketChannel 、Selector 在哪里?事件是什么时候注册的?我们扒开裤子看个究竟。
Netty 对 Java NIO API 的封装
接下来,看一下Netty是怎么对Java NIO API进行封装的。
ServerSocketChannel 在 Netty 中的体现
关于 ServerSocketChannel 的创建,直接找绑定端口的方法,如下图
图片
在 NioServerSocketChannel 类中可以看到 newChannel() 函数创建了 ServerSocketChannel 。
Selector 在 Netty 中的体现
看了上文《听说96.5%的程序员都没用过Netty》,可以知道 EventLoop 负责处理各种事件,所以可以盲猜一下,Selector 应该是在 NioEventLoopGroup 中创建的
图片
果不其然,在 NioEventLoopGroup 的构造方法中调用 JDK 的 SelectorProvider 创建了Selector,也就是 Java NIO 的代码。
事件注册在 Netty 中的体现
ServerSocketChannel 在创建后为其分配了一个 EventLoop 并开启新的线程(这也是Netty 多线程异步的体现),最终在 doRegister() 调用了JDK 的接口注册了Selector 并监听了事件,看见 selectionKey 应该什么都清楚了吧。
图片
事件处理在 Netty 中的体现
既然 ServerSocketChannel 注册了Selector 并监听了事件,那接下来就是 EventLoop 对事件的处理了。直接看 NioEventLoop 中的代码,因为他是通过新的线程启动的,所以直接看 run()
图片
processSelectedKeysPlain() 中的代码熟悉吧,Selector 监听到了某个事件进行处理。下面是对读事件的处理
图片
图中 ChannelPipeline 采用了责任链模式,是对事件的处理通道,会对声明时的ChannelPipeline依次执行,也就是图[Netty 编程]中的p.addlast。
Netty 网络编程流程
最后,通过一张图梳理下 Netty 的工作流程,并体现和 Java NIO API的关系。
图片
总结
在接触 Netty 的之前一定要先掌握 Java NIO,不然云里雾里的。
本文只是介绍了 Java NIO 在 Netty 中的体现、Netty 对 Java NIO 的封装,让大家更方便的理解 Netty,并不涉及 Netty 的高效、强大的设计之处,下文将会对此进行介绍。
本文转载自微信公众号「Hi程序员」,可以通过以下二维码关注。转载本文请联系Hi程序员公众号。