Netty简介
Netty是 一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。Netty是基于nio的,它封装了jdk的nio,让我们使用起来更加方法灵活。
Netty特点
高并发:Netty 是一款基于 NIO(Nonblocking IO,非阻塞IO)开发的网络通信框架,对比于 BIO(Blocking I/O,阻塞IO),他的并发性能得到了很大提高。
传输快:Netty 的传输依赖于零拷贝特性,尽量减少不必要的内存拷贝,实现了更高效率的传输。
封装好:Netty 封装了 NIO 操作的很多细节,提供了易于使用调用接口。
适配强:预置了多种编解码功能,支持多种主流协议,可以通过 ChannelHandler 对通信框架进行灵活地扩展。
Netty高性能
IO 线程模型:同步非阻塞,用最少的资源做更多的事。
内存零拷贝:尽量减少不必要的内存拷贝,实现了更高效率的传输。
内存池设计:申请的内存可以重用,主要指直接内存。内部实现是用一颗二叉查找树管理内存分配情况。
串形化处理读写:避免使用锁带来的性能开销。
高性能序列化协议:支持 protobuf 等高性能序列化协议。
Netty的非阻塞I/O复用模型
Netty的组件
Channel:Netty 网络操作抽象类,它除了包括基本的 I/O 操作,如 bind、connect、read、write 等。
EventLoop:主要是配合 Channel 处理 I/O 操作,用来处理连接的生命周期中所发生的事情。
ChannelFuture:Netty 框架中所有的 I/O 操作都为异步的,因此我们需要 ChannelFuture 的 addListener()注册一个 ChannelFutureListener 监听事件,当操作执行成功或者失败时,监听就会自动触发返回结果。
ChannelHandler:充当了所有处理入站和出站数据的逻辑容器。ChannelHandler 主要用来处理各种事件,这里的事件很广泛,比如可以是连接、数据接收、异常、数据转换等。
ChannelPipeline:为 ChannelHandler 链提供了容器,当 channel 创建时,就会被自动分配到它专属的 ChannelPipeline,这个关联是永久性的。
服务端和客户端创建流程。
netty 服务端创建的流程
- ServerBootstrap NIO 服务端启动辅助类,设置各种必要的参数 使用builder模式,解决构造函数参数过多并且不确定问题
- EventLoopGroup selector 线程池,系统中有两个Reactor线程组
(1)服务端用于监听和接收客户端连接的Reactor线程组
(2)处理I/O读写的Reactor线程组
3.绑定NioServerSocketChannel 服务端channel
4.设置TCP连接参数,backlog 表示的是未连接队列(当前连接服务器处于SYN_RECV状态)和 已完成连接队列(ESTABLISHED状态) 两个队列总和的最大值
5.TCP 链路建立时创建ChannelPipeline()
6.添加并设置ChannelHandler(),启动辅助类和父类分别指定handler,父类的handler作用是工厂类,为每一个客户端创建一个handler,辅助类的作用是所有监听端口的客户端都会执行它。
7.绑定并启动监听端口
(1)initAndRegister 创建,初始化和NioServerSocketChannel
(2)将其注册到Reactor上, 注册方式使用promise,启动异步线程进行注册。
8.Selector轮询。 由Reactor线程NioEventLoop负责调度和执行Selector轮询操作,选择准备就绪的Channel集合.
9.执行ChannelPipeline的相应的方法, 最终调度并执行ChannelHandler。执行用户自定义的ChannelHandler
客户端创建流程
- Bootstrap实例
- 创建处理客户端连接、I/O读写的Reactor线程NioEventLoopGroup
- 创建NioSocketChannel
- 创建默认的Channel Handler Pipeline, 用于调度和执行网路事件
- 发起TCP连接, 判断连接是否成功。如果成功,将NioSocketChannel注册到多路复用上,监听读操作位
- 注册对应的网络监听状态位到多路复用器
- 多路复用器在I/O现场中轮询各Channel, 处理连接结果。
- 如果连接成功,设置Future结果, 发送连接成功事件, 触发ChannelPipeline执行
- 由ChannelPipeline 调度执行系统和用户的ChannelHandler, 执行业务逻辑。
使用netty,为何不使用jdk自带nio?
NIO的类库和API繁杂,使用麻烦,你需要熟练掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等。
(1)需要具备其它的额外技能做铺垫,例如熟悉Java多线程编程,因为NIO编程涉及到Reactor模式,你必须对多线程和网路编程非常熟悉,才能编写出高质量的NIO程序
(2)可靠性能力补齐,开发工作量和难度都非常大。例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常码流的处理等等,NIO编程的特点是功能开发相对容易,但是可靠性能力补齐工作量和难度都非常大
(3)JDK NIO的BUG,例如臭名昭著的epoll bug,它会导致Selector空轮询,最终导致CPU 100%。官方声称在JDK1.6版本的update18修复了该问题,但是直到JDK1.7版本该问题仍旧存在,只不过该bug发生概率降低了一些而已,它并没有被根本解决