Netty 是一个异步事件驱动的网络应用程序框架,用于快速开发高性能、高可靠的网络 IO
程序。Netty 是基于 NIO 的,它封装了 jdk 的 NIO,让我们使用起来更加方法灵活。
Netty
是一个异步的、基于事件驱动的网络应用框架,用以快速开发高性能、高可靠性的网络 IO
程序。Netty
本质是一个基于 Java NIO 的框架,适用于服务器通讯相关的多种应用场景。Netty
主要针对在 TCP
协议下,面向客户端的高并发应用,或者 Peer-to-Peer
场景下的大量数据持续传输的应用。JDK NIO
的 Bug
:臭名昭著的 epoll bug。它会导致 Selector 空轮询,最终导致 CPU 100%。直到 JDK1.7 版本依然没得到根本性的解决。Netty 可构建高性能、低延时的各种 Java 中间件,例如 MQ、分布式服务框架、ESB消息总线等,Netty 主要作为基础通信框架提供高性能、低子延时的通信服务;典型的应用有:阿里分布式服务框架 Dubbo
的 RPC
框架使用 Dubbo
协议进行节点间通信,Dubbo
协议默认使用 Netty
作为基础通信组件,用于实现各进程节点之间的内部通信;还有 RocketMQ 也是使用 Netty 作为通讯的基础。
Netty
作为高性能的基础通信组件,提供了 TCP/UDP
和 HTTP
协议栈,方便定制和开发私有协议栈,账号登录服务器。
高并发:Netty 是基于 Java NIO(Nonblocking IO,非阻塞IO)开发的网络通信框架,对比于 BIO(Blocking I/O,阻塞IO),它的并发性能更高。
传输快:Netty 的传输依赖于零拷贝特性,尽量减少不必要的内存拷贝,实现了更高效率的传输。
方便易用:Netty 封装了 NIO 操作的很多细节,提供了易于使用调用接口。
Reactor 模式和通道紧密相关,反应器(EventLoop)的查询和分发的 IO 事件都来自 Channel 组件。
Channel
提供异步的网络 I/O
操作(如建立连接,读写,绑定端口),异步调用意味着任何 I/O
调用都将立即返回,并且不保证在调用结束时所请求的 I/O
操作已完成。
不同协议、不同的阻塞类型的连接都有不同的 Channel
类型与之对应。
Netty
基于 Selector
对象实现 I/O
多路复用,每一个 Reactor 线程 (NioEventLoop) 可以通过各自的 Selector
监听多个连接的 Channel
事件。
当向一个 Selector
中注册 Channel
后,Selector
内部的机制就可以自动不断地轮询这些注册的 Channel
是否有已就绪的 I/O
事件(例如可读,可写,网络连接完成等),这样程序就可以很简单地使用一个线程高效地管理多个 Channel
。
无论是服务端代码中自定义的 NettyServerHandler,还是客户端代码中自定义的 NettyClientHandler,都继承于 ChannelInboundHandlerAdapter,ChannelInboundHandlerAdapter 又继承于 ChannelHandlerAdapter,ChannelHandlerAdapter 又实现了 ChannelHandler 接口。
ChannelHandler
是一个接口,处理 I/O
事件或拦截 I/O
操作,并将其转发到其 ChannelPipeline
中的下一个 ChannelHandler。因此多个 ChannelHandler 形成一个责任链,责任链位于 ChannelPipeline 中。
ChannelPipeline
是一个 Handler
的集合,它负责处理和拦截 Channel
的入站事件和出站操作,相当于一个贯穿 Netty
的链。ChannelPipeline
实现了一种高级形式的拦截过滤器模式,使用户可以完全控制事件的处理方式,以及 Channel
中各个的 ChannelHandler
如何相互交互。
在 Netty
中每个 Channel
都有且仅有一个 ChannelPipeline
与之对应,它们的组成关系如下:
一个 Channel 包含了一个 ChannelPipeline,而 ChannelPipeline 中又维护了一个由 ChannelHandlerContext 组成的双向链表,并且每个 ChannelHandlerContext 中又关联着一个 ChannelHandler。入站事件和出站事件在一个双向链表中,入站事件会从链表 head 往后传递到最后一个入站的 handler,出站事件会从链表 tail 往前传递到最前一个出站的 handler,两种类型的 handler 互不干扰。
NioEventLoop
内部采用串行化设计,从消息的 读取->解码->处理->编码->发送,始终由 IO
线程 NioEventLoop
负责。
在 Netty 中,一个 EventLoop 相当于一个子反应器(SubReactor),一个 NioEventLoop 子反应器拥有一个事件轮询线程。
每一个 NioEventLoop 拥有一个 Java NIO 选择器以及一个 taskQueue ,Selector 用于实现 I/O 多路复用,taskQueue 的目的是在任务提交的速度大于线程的处理速度的时候起到缓冲作用,或者用于异步处理 Selector 监听到的 IO 事件。如果 Handler 有一些长时间的业务处理,可以交给 taskQueue 异步处理。
同时 Selector 以及 taskQueue 共用一个线程,用单线程的方式串行执行队列中的 task。
多个 EventLoop 线程放在一起,可以组成一个 EventLoopGroup。EventLoopGroup
提供 next
接口,可以从组里面按照一定规则(如轮询)获取其中一个 EventLoop
来处理任务。
为了及时接收到新连接,在服务器端,一般有两个独立的反应器,一个负责新连接的监听和接收,另一个负责 IO 事件轮询和分发。对应到 Netty 的服务器程序中,则需要设置两个 EventLoopGroup 线程组,一个 EventLoopGroup 负责新连接的监听和接收——Boss 线程组;另一个负责 IO 事件轮询和分发,并执行 Handler 处理器中的业务处理——Worker 线程组。