netty:高性能NIO框架-记录
概述
netty基于NIO,NIO基于epoll系统调用(linux下)
I/O多路复用中的epoll
由于poll和select所能打开的FD数目受到限制,随意改大会影响网络处理,而epoll是跟随系统内存的,改变不了,可通过cat /proc/sys/fs/file-max
查看。
epoll的改进如下
- 支持一个进程打开的FD仅受到系统最大文件句柄数限制
- 效率不会随着FD的增加而线性下降:因为epoll是根据fd上面的callback实现的,只有活跃的fd才会调用callback,而idle状态的不会。类似伪AIO
- 使用mmap加速内核空间和用户空间的消息传递
java的IO发展
操作系统已经提供了五种IO的处理方式,但是JAVA是逐渐提供的
BIO
传统的BIO是服务器一个客户端建立一个线程
伪异步I/O
将每个连接作为task放入线程任务池中去执行。
BIO和伪异步IO底层都是使用阻塞的read(),write()系统调用去执行。
NIO
Buffer
NIO中的读写操作都是去读写缓冲区,Buffer的底层是数组,但是提供了其他一些方便操作数组的功能。
Channel
全双工,正好映射了操作系统的全双工通信。分为两大类,文件读写的和网络读写的。
Selector
不断的轮询注册在其上的Channel,如果Channel上发生读/写事件,这个Channel就处于就绪状态,可以通过SelectionKey来获取就绪Channel的集合。
NIO2.0
称为AIO,对应于unix中的事件驱动IO
netty基础
netty的基本数据流程图如下:
所以,ChannelHandler就是相当于业务逻辑处理。
Channel
对标socket
生命周期
- ChannelUnregistered:已创建,但还没注册到EventLoop
- ChannelRegistered:被注册到了EventLoop
- ChannelActive:已经连接到远程节点,可以收发数据了。
- ChannelInactive:没有连接到远程节点
当Channel的状态改变时,会生成事件转发给handler.
ChannelHandler
生命周期
- handlerAdded:handler添加到pipeLine时调用
- handlerRemoved:从pipeLine移除时调用
- exceptionCaught:当有错误时产生
EventLoop
控制流、多线程处理、并发
EventLoop、EventLoopGroup、Channel之间的关系如下图:
一个eventLoop可以对应多个channel,一个eventLoop就是一个线程。
服务器端添加两个group,是用的Reactor模式的主从模式,一个group处理连接,一个group处理读写。
ChannelFuture
异步通知
为其添加Listener以接收通知。
ChannelPipeLine
包含了出站和入站的handler,入站的一端是ChannelPipeLine的头部,出站的一端是尾部。出站和入站对于客户端和服务器是相对的。
当ChannelHandler被添加到pipeLine后,他会被分配一个ChannelHandlerContext,代表了该handler和pipeLine的绑定。
ctx可以获取channel,但一般还是用于写出站数据的。
Netty,有两种发送消息的方式(就是出战么),一种是写到channel中,那么消息会从pipeLine的尾端移动;一种是写到ctx中,那么消息会从下一个Handler移动。
channel、channelPipeline、ChannelHandler、ChannelHandlerContext,eventLoop的关系
每一个channel都和唯一的pipeLine绑定,每个pipeLine中有多个handler,ctx使得handler可以和其他handler交互或者和它的pipeLine交互。
每一个handler都是通过它的eventLoop来处理传递给它的事件的。
一个handler可以存在于多个pipeLine中,一个handler只和ctx绑定。如果以map来看的话,一个handler的名称就是键名,其值就是ctx
引导
引导是将eventLoop、pipeLine、Handler组织起来的。
服务端:ServerBootstarp,可以有两个EventLoopGroup;客户端:Bootstarp,只有一个引导。
服务端两个group的情况,如下图:
左边是负责创建channel并且和一个EventLoop绑定,右边是一旦连接建立了,第二个group就会给该channel创建一个EventLoop.
ByteBuf
主要是根据两个索引,writerIndex和readerIndex来确定几个区域。
netty解码器
为了解决粘包、拆包的问题。
TCP没有消息边界,所以对粘包、拆包的处理只能在应用层协议上。
TCP发生粘包、拆包的原因
- 应用程序系统调用
write
的发送长度大于系统发送缓冲区大小 - 进行mss大小的TCP分段;mss就是TCP最大段大小(最大长度),mss=mtu-40(在同一个网段中才是这样子,不再同一个网段就不能确定mss值,所以才引出来第三点)
- MTU的分段
TCP粘包、拆包解决
- 定长消息
- 回车换行符作为结束符
- 特殊结束符
- 头部定义字段标识消息的总长度
netty中为这四种提供了四个解码器。