Netty 核心组件笔记
Netty是一款高效的NIO框架和工具,基于JAVA NIO提供的API实现。
在JAVA NIO方面Selector给Reactor模式提供了基础,Netty结合Selector和Reactor模式设计了高效的线程模型,Reactor模式的参与者主要有下面一些组件:
Channel
- Selector
EventLoopGroup/EventLoop
ChannelPipeline
Channel
在网络IO方面,Channel的主要实现是ServerSocketChannel和SocketChannel。他们都代表一个面向流的可监听读写事件的socket。ServerSocketChannel是用于服务器端的socket,他提供了一个静态工具方法open来为用户提供获取Channel的工具:
public static ServerSocketChannel open() throws IOException {
return SelectorProvider.provider().openServerSocketChannel();
}
ServerSocketChannel的使用方式是面向服务器端的,一般的开发流程是:
获取一个ServerSocketChannel。
设置网络操作,这些参数主要是和TCP协议有关。
将ServerSocketChannel注册到Selector(多路复用器)。
将ServerSocketChannel和某个具体的地址绑定。
用户像多路复用器设置感兴趣的IO事件。
用户线程以阻塞或非阻塞方式轮询Selector来查看是否有就绪的IO事件。
用户针对不同的IO事件对Channel进行具体的IO操作。
SocketChannel主要是面向客户端的开发的,也是以open方式获取channel,客户端的开发流程大致如下:
获取一个SocketChannel。
设置Channel为非阻塞方式。
获取Selector。
将channel注册到Selector,并监听CONNECT事件。
调用channel的connect方法连接指定的服务器和端口。
如果连接成功则进行IO操作,如果没成功则轮询Selector处理CONNECT事件。
Selector
Selector是JAVA NIO提供的SelectableChannel多路复用器,它内部维护着三个SelectionKey集合,负责配合select操作将就绪的IO事件分离出来,落地为SelectionKey,我前面有一篇文章的一部分对Selector进行了相对详细的介绍(这里)。
在Netty线程模型中,我认为Selector充当着demultiplexer的角色,而对于SelectionKey我们可以将它看成Reactor模式中的资源。
EventLoopGroup/EventLoop
EventLoopGroup是一组EventLoop的抽象,由于Netty对Reactor模式进行了变种,实际上为更好的利用多核CPU资源,Netty实例中一般会有多个EventLoop同时工作,每个EventLoop维护着一个Selector实例,类似单线程Reactor模式地工作着。
EventLoopGroup提供next接口,可以总一组EventLoop里面按照一定规则获取其中一个EventLoop来处理任务.
1. BossEventLoopGroup通常是一个单线程的EventLoop,EventLoop维护着一个注册了ServerSocketChannel的Selector实例,
2. BoosEventLoop不断轮询Selector将连接事件分离出来,通常是OP_ACCEPT事件,
3. 然后将accept得到的SocketChannel(注意不是ServerSocketChannel)交给WorkerEventLoopGroup,
4. WorkerEventLoopGroup会由next选择其中一个EventLoopGroup来将这个SocketChannel注册到其维护的Selector并对其后续的IO事件(OP_READ, OP_WRITE等)进行处理。
在Reactor模式中BossEventLoopGroup主要是对多线程的扩展,而每个EventLoop的实现涵盖IO事件的分离,和分发(Dispatcher)。
定时任务和一般任务
在Netty的EventLoop线程中,这个线程主要需要处理IO事件和其他两种任务,分别为定时任务和一般任务。Netty提供可一个参数ioRatio用于用户调整单线程对于IO处理时间和任务处理时间的分配的比率。这样根据实际应用场景用户可以对这个值进行调整,默认值是50,也就是这个线程会将处理IO的时间和处理任务的时间控制为1:1。
final long ioStartTime = System.nanoTime(); processSelectedKeys();//处理IO事件 final long ioTime = System.nanoTime() - ioStartTime;//处理IO事件的时间
runAllTasks(ioTime * (100 - ioRatio) / ioRatio);//计算用于处理任务的时间
这样尽管一个EventLoop会关联多个Channel,这些Channel在单个线程下并不会出现并发问题, 而是串行的顺序执行,同时对于异步任务的处理也一样,Netty这样设计即免去了并发问题的烦恼,有减少了多线程上下文切换带来的性能损耗,同时基于EventLoopGroup实现的有限的线程数能够充分利用CPU处理能力。
ChannelPipeline
在Netty中ChannelPipeline维护着一个ChannelHandler的链表队列,每个SocketChannel都有一个维护着一个ChannelPipeline实例,而每个ChannelPipeline实例通常维护着一个ChannelHandler链表队列,由于SocketChannel是和SelectionKey关联的,也就是Reactor模式中的资源,当EventLoop将SelectionKey分离出来的时候会将SelectionKey关联的Channel交给Channel关联的ChannelHandler链来处理,那么ChannelPipeline其实是担任着Reactor模式中的请求处理器这个角色。
ChannelPipeline的默认实现是DefaultChannelPipeline,DefaultChannelPipeline本身维护着一个用户不可见的tail和head的ChannelHandler,他们分别位于链表队列的头部和尾部。tail在更上从的部分,而head在靠近网络层的方向。
在Netty中关于ChannelHandler有两个重要的接口,ChannelInBoundHandler和ChannelOutBoundHandler。inbound可以理解为网络数据从外部流向系统内部,而outbound可以理解为网络数据从系统内部流向系统外部。用户实现的ChannelHandler可以根据需要实现其中一个或多个接口,将其放入Pipeline中的链表队列中,ChannelPipeline会根据不同的IO事件类型来找到相应的Handler来处理,同时链表队列是责任链模式的一种变种,自上而下或自下而上所有满足事件关联的Handler都会对事件进行处理.
其他扩展
select、poll和epoll
JAVA对NIO的支持是从1.4版本开始的,是基于多路复用技术,而在linux操作系统方面多路复用技术有三种常用的机制:select、poll和epoll,epoll的支持也只是linux2.6版本之后才提供,java在jdk5.0的update 9之后才对epoll进行支持。这三种机制本质上都是同步IO,主要是由于他们都需要在读写事件就绪的时候需要自己进行读写,也就是这个这个读写过程是阻塞的。下面对着三种机制进行简单总结:
select函数:改函数允许进程指示内核等待多个事件中的任何一个发生的时候或者在一定时间之后被唤醒,select有个致命的缺点即在多路复用中文件描述符的数量有限制,如果需要突破限制需要重新编译操作系统内核。
poll函数:poll机制与select机制类似,区别是poll没有最大描述符限制。
epoll函数:epoll在linux2.6内核中被提出来,是之前的select和poll的增强版本。epoll也没有文件描述符数量限制,而且是用一个文件描述符来管理多个描述符。在性能上相比上面两种有了很大的优化。
epoll原理图如下:
epoll使用“事件”的方式通知用户程序数据就绪,并且使用内存拷贝的方式使用户程序直接读取内核准备好的数据,不用再读取数据
ByteBuffer
JAVA NIO直接和Channel打交道的Buffer是ByteBuffer,ByteBuffer接口提供主要的内存分配、IO读写等相关接口。
值得注意的是JAVA NIO提供了两种Buffer内存分配机制,一种是堆内存,另一种是直接内存,主要区别:
堆内存分配和回收比较快,但是网络数据需要从内核copy到堆中。
直接内存分配和回收比较慢,但是免去了从内核copy到堆中的一次copy。
这两种内存各有千秋,使用的时候要根据实际情况去选择。
参考: https://my.oschina.net/andylucc/blog/614295
Netty 核心组件笔记的更多相关文章
- Netty学习笔记(二)——netty组件及其用法
1.Netty是 一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端. 原生NIO存在的问题 1) NIO的类库和API繁杂,使用麻烦:需要熟练掌握Selector.Se ...
- Netty学习笔记(二) 实现服务端和客户端
在Netty学习笔记(一) 实现DISCARD服务中,我们使用Netty和Python实现了简单的丢弃DISCARD服务,这篇,我们使用Netty实现服务端和客户端交互的需求. 前置工作 开发环境 J ...
- Netty 学习笔记(1)通信原理
前言 本文主要从 select 和 epoll 系统调用入手,来打开 Netty 的大门,从认识 Netty 的基础原理 —— I/O 多路复用模型开始. Netty 的通信原理 Netty 底层 ...
- Netty 核心组件 Pipeline 源码分析(二)一个请求的 pipeline 之旅
目录大纲: 前言 针对 Netty 例子源码做了哪些修改? 看 pipeline 是如何将数据送到自定义 handler 的 看 pipeline 是如何将数据从自定义 handler 送出的 总结 ...
- Netty核心组件介绍及手写简易版Tomcat
Netty是什么: 异步事件驱动框架,用于快速开发高i性能服务端和客户端 封装了JDK底层BIO和NIO模型,提供高度可用的API 自带编码解码器解决拆包粘包问题,用户只用关心业务逻辑 精心设计的Re ...
- Netty学习笔记-入门版
目录 Netty学习笔记 前言 什么是Netty IO基础 概念说明 IO简单介绍 用户空间与内核空间 进程(Process) 线程(thread) 程序和进程 进程切换 进程阻塞 文件描述符 文件句 ...
- 【Netty】Netty核心组件介绍
一.前言 前篇博文体验了Netty的第一个示例,下面接着学习Netty的组件和其设计. 二.核心组件 2.1. Channel.EventLoop和ChannelFuture Netty中的核心组件包 ...
- Netty学习笔记(六) 简单的聊天室功能之WebSocket客户端开发实例
在之前的Netty相关学习笔记中,学习了如何去实现聊天室的服务段,这里我们来实现聊天室的客户端,聊天室的客户端使用的是Html5和WebSocket实现,下面我们继续学习. 创建客户端 接着第五个笔记 ...
- Netty 核心组件 EventLoop 源码解析
前言 在前文 Netty 启动过程源码分析 (本文超长慎读)(基于4.1.23) 中,我们分析了整个服务器端的启动过程.在那篇文章中,我们重点关注了启动过程,而在启动过程中对核心组件并没有进行详细介绍 ...
随机推荐
- 解读NoSQL数据库的四大家族
在目前的企业IT架构中,系统管理员以及DBA都会考虑使用NoSQL数据库来解决RDBMS所不能解决的问题,特别是互联网行业.传统的关系型数据库主要以表(table)的形式来存储数据,而无法应对非结构化 ...
- TCP客户端图片上传服务端保存本地示例
//TCP客户端public class TCPClient { public static void main(String[] args)throws IOException { Socket s ...
- 【JavaScript 6连载】六、认识原型
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...
- byte & 0xff char 转换
https://blog.csdn.net/lixingtao0520/article/details/75450883 版权声明:本文为博主原创文章,转载请注明作者与出处,http://blog.c ...
- bzoj1606
题目链接 反着循环就是每个东西只能选一次 #include<iostream> #include<cstdio> #include<cstdlib> #includ ...
- Django之MVC和MTV
一. MVC MVC 是一种使用 MVC(Model View Controller 模型-视图-控制器)设计创建 Web 应用程序的模式: Model(模型)表示应用程序核心(比如数据库记录列表). ...
- 每日linux命令学习-引用符号(反斜杠\,单引号'',双引号"")
引用符号在解析器中保护特殊元字符和参数扩展,其使用方法有3种:反斜杠(\),单引号(’‘),双引号(“”). 单引号和双引号必须匹配使用,均可在解析器中保护特殊元字符和通配符,但是单引号(硬转义)主要 ...
- Servlet向JSP过渡
表格中添加删除链接删除相关数据,在servlet这里用的是纯java代码,在纯java代码里面的输出里面添加类似前端拼接的东西.(删除链接,并根据id来删除相应数据)这里使用的纯servlet. 在o ...
- 9i时候的块
Header (Common and Variable) The header contains general block information, such as the block addres ...
- PHP使用http_build_query()构造URL字符串的方法(可将POST参数组转换拼接成GET请求链接)
<?php //parse_str与http_build_query的使用 //使用parse_str将url字符串转变为key=>value的数组 $str = "tn=mon ...