netty简介
netty是一个异步的、基于事件驱动的网络应用框架,用来快速开发高性能、高可靠的网络IO程序,主要针对在TCP协议下,面向客户端的高并发应用,本质是一个NIO框架
三种I/O模型
目前java支持三种I/O模型,BIO、NIO、AIO
BIO
同步阻塞IO,一个连接请求对应一个线程,如果该连接没有做任何事情会造成不必要的开销,在java中就是服务端创建一个ServerSocket,然后客户端用一个Socket去连接服务端的ServerSocket,ServerSocket接收到一个连接请求就创建一个Socket和一个线程去跟客户端Socket进行通讯,客户端发送一个请求,服务端进行处理后返回响应,在响应返回前,客户端进行阻塞等待
缺点是每次一个客户端接入,都需要在服务端创建一个线程来对这个客户端进行服务
什么算作阻塞
系统调用read
从socket里读取一段数据时,由于Linux无法知道网络上对方是否会发数据。如果没数据发过来,对于调用read
的程序来说,就只能进行等待,直到收到数据,当等待时,整个线程会被挂起,无法执行,也无法做其他的工作
NIO
同步非阻塞IO,基于Reactor模型实现的,一个线程处理多个连接请求,客户端发送的连接请求会注册到多路复用器上,多路复用器轮询到连接有I/O请求进行处理
在调用read时,如果发现没有数据到达,BIO模式下会直接block,而NIO模式下会立刻返回-1,并且errno设为EAGAIN,且与IO多路复用结合,来提供一种通知机制,epoll是linux中的IO多路复用的实现
AIO
异步非阻塞IO,基于Proactor模型实现,引入了异步通道的概念,每个连接发送过来的请求,都会绑定一个Buffer,然后通知操作系统去完成异步的读,等到操作系统完成读之后,会调用服务端接口,来对数据进行处理,并进行数据回写,在进行数据回写时同样是给操作系统一个Buffer,让操作系统去完成写,写完了之后在通知客户端来读取数据,特点是先由操作系统完成后才通知服务端程序启动线程去处理,适用于连接数较多且连接时间较长的应用
为什么需要Netty
既然已经有了NIO,为什么还需要Netty对其进行再次封装呢?
- NIO的类库和API过于繁琐
- 开发工作量和难度太大,如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞、异常流处理等情况
- 在JDK7中存在Epoll bug,会导致Selector空轮询,最终导致CPU 100%
注意:Netty5.x已经不在维护了,需要使用Netty4.x
netty构成
核心组件
EventLoop和NioEventLoopGroup
EventLoop用于处理Channel的IO操作,一个单一的EventLoop通常会处理多个Channel事件,一个EventLoopGroup可以含有一组的EventLoop和提供了一种迭代用于检索清单中的下一个
1 | public interface EventLoopGroup extends EventExecutorGroup { |
NioEventLoopGroup是用来处理IO操作的多线程事件循环器,Netty提供了不同的EventLoopGroup的实现来处理不同的传输
Bootstrap和ServerBootstrap
Bootstrap和ServerBootstrap是一个启动NIO服务的辅助启动类,提供了一个用于应用程序网络层配置的容器
Bootstrap是用于客户端的,用于连接到远程主机和端口,只有一个EventLoopGroup
1 | public B group(EventLoopGroup group) { |
ServerBootstrap是用于服务端的,用于绑定本地端口,有两个EventLoopGroup
1 | public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) { |
且其内有两个channel集合,第一个集合包含一个单例ServerChannel,代表持有一个绑定了本地端口的socket
1 | serverBootstrap.channel(NioServerSocketChannel.class) |
第二个集合是包含了所有创建的channel,用来处理服务器所接收到的客户端进来的连接
1 | serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() { |
Channel
Channel定义了与socket丰富交互的操作集:bind、close、config、connect、isActive、isOpen、isWritable、read、write等
ChannelHandler
ChannelHandler支持很多协议,并且提供用于数据处理的容器,由特定事件触发,可用于几乎所有的动作,如将一个对象转为字节、执行过程中抛出的异常处理等。
常用接口如ChannelInboundHandler,该接口接收到入站事件可以处理应用程序逻辑
ChannelPipeline
ChannelPipeline提供了一个ChannelHandler链容器,并提供了一个API用于管理沿着链入站和出站事件的流动,每个Channel都有自己的ChannelPipeline,当Channel创建时自动创建的
其内部是一个ChannelHandlerContext的双向链表
1 | volatile AbstractChannelHandlerContext next; |
ChannelHandler会在程序的引导阶段被添加到ChannelPipline中,并且被添加的顺序将决定处理数据的顺序
ChannelFuture
Netty所有IO操作都是异步的,Netty提供了ChannelFuture接口来解决异步操作无法立即返回结果,通过addListener来注册一个ChannelFutureListener,当操作完成后,会进行通知