操作系统对IO的支持
对Linux系统来说,所有文件、socket的操作都是针对文件描述符的
对IO的操作分为5种
– 阻塞IO——调用recvfrom时,如果没有任何传入信息,则调用被阻塞,有传入信息时,调用
返回
– 非阻塞IO——recvfrom没有数据的话,返回一个错误,轮询检查错误状态,看是否有数据
– IO复用——提供select/poll方式,进程对每个socket的fd顺序扫描,查看是否就绪,阻塞
在select上,如果就绪,调用recvfrom,同时提供epoll,不是顺序扫描,而是提供回调
函数,当fd就绪时,调用回调函数,进一步提高效率
– 信号驱动IO——当数据就绪时,生成SIGIO信号
用户连接请求
– 如果是TCP,由于有连接请求,需要等待连接进入(accept),通过多线程处理
– 如果是UDP,不需要
– Netty通过NioEventLoopGroup引入了对accept线程池的支持
TCP/UDP连接后线程处理
– 通过多线程/线程池的组合提高系统响应速度
– 要注意不要随便在input处理中引入额外的线程
业务逻辑的多线程处理
– 这部分是akka/disruptor/kafka这类库/框架的用武之地,而netty/mina则是在前面两
点提供了高性能的支持
– 业务逻辑尤其是和第三方后台打交道的业务逻辑并不是通过一个线程就可以提高效率的,尤其
是习惯于编写java web应用的程序员需要注意
数据库
– 通过slave/master、连接池等多种手段进行优化
Netty网络编程
Netty的优势
– 使用更高效的socket底层
– 对epoll空轮询引起的CPU占用飙升在内部进行了处理,避免程序员直接使用NIO的陷阱
– 采用多种decoder/encoder支持,对粘包/分包进行自动化处理
– 可使用接受/处理线程池,提高连接效率
– 对重连、心跳检测的简单支持
– 对Protocol buffer以及java serialization协议编解码的直接支持
– 简化NIO的处理方式
Netty的核心在于整个的channel pipeline
– Pipeline是一个队列,但是包含了两种处理器,一种是
ChannelInboundHandlerAdapter,一种是ChannelOutboundHandlerAdapter
– ChannelInboundHandlerAdapter负责处理进入的ByteBuf或者其它内容
– ChannelOutboundHandlerAdapter负责处理输出的ByteBuf或者其它内容
– Pipeline是一个ChannelHandler的队列
• 在输入时,按照从头至尾的顺序,遍历所有的ChannelInboundHandler
• 在输出时,按照从尾至首的顺序,遍历所有的ChannelOutboundHandler
– 两个NioEventLoopGroup分别创建两个线程池,负责accept和read
– DelimiterBasedFrameDecoder,LineBasedFrameDecoder,
FixedLengthFrameDecoder和LengthFieldBasedFrameDecoder分别代表:
• 分隔符标记的消息组
• 换行符标记的消息组(特殊分隔符)
• 固定长度消息组
• 长度开始的消息组
对Linux系统来说,所有文件、socket的操作都是针对文件描述符的
对IO的操作分为5种
– 阻塞IO——调用recvfrom时,如果没有任何传入信息,则调用被阻塞,有传入信息时,调用
返回
– 非阻塞IO——recvfrom没有数据的话,返回一个错误,轮询检查错误状态,看是否有数据
– IO复用——提供select/poll方式,进程对每个socket的fd顺序扫描,查看是否就绪,阻塞
在select上,如果就绪,调用recvfrom,同时提供epoll,不是顺序扫描,而是提供回调
函数,当fd就绪时,调用回调函数,进一步提高效率
– 信号驱动IO——当数据就绪时,生成SIGIO信号
– 异步IO——当内核完成整个操作时(将数据从内核拷贝至用户缓冲区之后),发出通知
Java NIO
Java NIO是基于epoll开发的非阻塞式IO
– 支持打开的socket数量仅受到操作系统最大文件句柄数量限制:传统的select/poll方式
的最大句柄数量为1024,如果需要改变,则需要修改FD_SETSIZE重新编译内核
– Select/epoll采用回调方式而不是顺序轮询,对于大多数连接不活动的情况下,效率远高
于select/poll方式,不会随着fd的数量增加而有较大的效率下降
– Select/poll/epoll都需要将内核将消息通知用户空间,需要进行内核和用户空间的内存
复制,epoll采用mmap方式进行内核和用户空间的内存共享
– NIO 2.0开始支持基于事件驱动的IO方式
– 基于epoll的实现可能会出现空轮询以至于CPU飙升至100%
服务器端编程常见瓶颈
用户连接请求
– 如果是TCP,由于有连接请求,需要等待连接进入(accept),通过多线程处理
– 如果是UDP,不需要
– Netty通过NioEventLoopGroup引入了对accept线程池的支持
TCP/UDP连接后线程处理
– 通过多线程/线程池的组合提高系统响应速度
– 要注意不要随便在input处理中引入额外的线程
业务逻辑的多线程处理
– 这部分是akka/disruptor/kafka这类库/框架的用武之地,而netty/mina则是在前面两
点提供了高性能的支持
– 业务逻辑尤其是和第三方后台打交道的业务逻辑并不是通过一个线程就可以提高效率的,尤其
是习惯于编写java web应用的程序员需要注意
数据库
– 通过slave/master、连接池等多种手段进行优化
Netty网络编程
Netty的优势
– 使用更高效的socket底层
– 对epoll空轮询引起的CPU占用飙升在内部进行了处理,避免程序员直接使用NIO的陷阱
– 采用多种decoder/encoder支持,对粘包/分包进行自动化处理
– 可使用接受/处理线程池,提高连接效率
– 对重连、心跳检测的简单支持
– 对Protocol buffer以及java serialization协议编解码的直接支持
– 简化NIO的处理方式
Netty的核心在于整个的channel pipeline
– Pipeline是一个队列,但是包含了两种处理器,一种是
ChannelInboundHandlerAdapter,一种是ChannelOutboundHandlerAdapter
– ChannelInboundHandlerAdapter负责处理进入的ByteBuf或者其它内容
– ChannelOutboundHandlerAdapter负责处理输出的ByteBuf或者其它内容
– Pipeline是一个ChannelHandler的队列
• 在输入时,按照从头至尾的顺序,遍历所有的ChannelInboundHandler
• 在输出时,按照从尾至首的顺序,遍历所有的ChannelOutboundHandler
– 两个NioEventLoopGroup分别创建两个线程池,负责accept和read
– DelimiterBasedFrameDecoder,LineBasedFrameDecoder,
FixedLengthFrameDecoder和LengthFieldBasedFrameDecoder分别代表:
• 分隔符标记的消息组
• 换行符标记的消息组(特殊分隔符)
• 固定长度消息组
• 长度开始的消息组