【博学谷学习记录】超强总结,用心分享|架构师Netty框架学习总结-创新互联
- 前言
- 一、Netty
- · Netty简介
- · Netty核心架构
- · Netty中Reactor实现
- 1.工作流程
- 2.ChannelPipeline 和 ChannelHandler(开发者关注实现点)
- · Netty一次编解码(ByteToMessage)——解决TCP粘包、拆包(半包)
- 1.TCP粘包、拆包(半包)
- 2.Netty解决TCP粘包、半包 (一次编解码)
- · Netty二次编解码(MessageToMessage)——对象转换 方便使用
- 1.二次编解码方式
- 2.常用的二次编解码器
- · Keepalive & idle监测 (心跳检测)
- 二、网络IO
- · IO概念
- · IO类型
- 阻塞 / 非阻塞
- 同步 / 异步
- · 常见IO模型
- 1.同步阻塞IO
- Java-BIO模型
- 2.同步非阻塞IO
- 3.IO多路复用
- Java-NIO模型
- 4.信号IO
- 5.异步IO
- Java-AIO模型
- 三、Reactor线程模型
- · 概念
- · NIO下Reactor线程模型
- 1.单Reactor单线程
- 2.单Reactor多线程
- 3.主从Reactor多线程
前言
本文介绍了基于NIO、Reactor线程模型的Netty网络通信框架,部分内容涉及个人的理解,可能存在理解偏差。
关键字快捷链接:NIO 、 主从Reactor多线程
一、Netty · Netty简介
Netty是由JBOSS提供的一个java开源框架,现为 Github上的独立项目。Netty提供非阻塞的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序,也就是说,Netty 是一个基于NIO的客户、服务器端的编程框架。
Netty官网链接
· Netty核心架构核心(Core)
· 可扩展的事件模型
· 统一的通信API,简化了通信编码
· 零拷贝机制与丰富的字节缓冲区
传输服务(Transport Services)
· 支持socket以及datagram(数据报)
· http传输服务
· In-VM Pipe (管道协议,是jvm的一种进程)
协议支持(Protocol Support)
· http 以及 websocket
· SSL 安全套接字协议支持
· Google Protobuf (序列化框架)
· 支持zlib、gzip压缩
· 支持大文件的传输
· RTSP(实时流传输协议,是TCP/IP协议体系中的一个应用层协议)
· 支持二进制协议并且提供了完整的单元测试
· Netty中Reactor实现 1.工作流程
Netty线程模型是基于Reactor模型实现的,对Reactor三种模式都有非常好的支持,并做了一定的改进,也非常的灵活,一般情况,在服务端会采用主从Reactor架构模型。
工作流程
- Netty 抽象出两组线程池:BossGroup 和 WorkerGroup,每个线程池中都有EventLoop 线程(可以是OIO,NIO,AIO)。BossGroup
中的线程专门负责和客户端建立连接,WorkerGroup 中的线程专门负责处理连接上的读写, EventLoopGroup 相当于一个事件循环组,
这个组中含有多个事件循环 - EventLoop 表示一个不断循环的执行事件处理的线程,每个EventLoop 都包含一个 Selector,用于监听注册在其上的 Socket 网络
连接(Channel)。 - 每个 Boss EventLoop 中循环执行以下三个步骤:
3.1 select:轮训注册在其上的 ServerSocketChannel 的 accept 事件(OP_ACCEPT 事件)
3.2 processSelectedKeys:处理 accept 事件,与客户端建立连接,生成一个SocketChannel,并将其注册到某个 Worker
EventLoop 上的 Selector 上
3.3 runAllTasks:再去以此循环处理任务队列中的其他任务 - 每个 Worker EventLoop 中循环执行以下三个步骤:
4.1 select:轮训注册在其上的SocketChannel 的 read/write 事件(OP_READ/OP_WRITE 事件)
4.2 processSelectedKeys:在对应的SocketChannel 上处理 read/write 事件
4.3 runAllTasks:再去以此循环处理任务队列中的其他任务 - 在以上两个processSelectedKeys步骤中,会使用 Pipeline(管道),Pipeline 中引用了 Channel,即通过 Pipeline 可以获取到对
应的 Channel,Pipeline 中维护了很多的处理器(拦截处理器、过滤处理器、自定义处理器等)。
开发者主要关注和实现的点是pipeline中的channelHandler,即数据处理业务操作等在channelHandler上进行。
ChannelPipeline 提供了 ChannelHandler 链的容器。以服务端程序为例,客户端发送过来的数据要接收,读取处理,我们称数据是入站的,需要经过一系列Handler处理后;如果服务器想向客户端写回数据,也需要经过一系列Handler处理,我们称数据是出站的。
一个SocketChannel对应一个ChannelPipeline,ChannelPipeline提供了双向链的ChannelHandler容器,初始化了head,tail的ChannelHandler。
入站:从head到tail
出站:从tail到head
ChannelHandler类型
- inbound——处理入站事件
- outbound——处理出站事件
- duplex——混合型处理入站和出站事件
对于数据的出站和入站,有着不同的ChannelHandler类型与之对应:
- ChannelInboundHandler 入站事件处理器
- ChannelOutBoundHandler 出站事件处理器
- ChannelHandlerAdapter提供了一些方法的默认实现,可减少用户对于ChannelHandler的编写
- ChannelDuplexHandler:混合型,既能处理入站事件又能处理出站事件。
粘包、拆包概念
粘包:发送方每次写入数据<套接字缓冲区大小 ; 接收方读取套接字缓冲区数据不够及时
拆包:发送方写入数据>套接字缓冲区大小 ; 发送的大报文长度大于MSS, 数据包大于协议的MTU(大传输单元,1500字节),必须拆包
收发角度:一个发送可能被多次接收(半包),多个发送可能被一次接收(粘包)
传输角度:一个发送可能占用多个传输包(半包),多个发送可能公用一个传输包(粘包)
粘包、拆包原因
根本原因: 消息无边界
TCP 协议是面向连接的、可靠的、基于字节流的传输层通信协议,是一种流式协议,消息无边界。
解决思路
解决TCP粘包、半包问题的根本:找出消息的边界
解决方案
- TCP变为短连接,一个请求一个短连接
- 每条数据都是固定长度
- 用分隔符作为标识
- 包装消息为 head+body (head描述body的长度等信息)
Netty提供了针对封装成帧这种形式下不同方式的拆包器,所谓的拆包其实就是数据的解码,所谓解码就是将网络中的一些原始数据解码成上层应用的数据,那对应在发送数据的时候要按照同样的方式进行数据的编码操作然后发送到网络中。
· Netty二次编解码(MessageToMessage)——对象转换 方便使用 1.二次编解码方式用户数据(ByteBuf )和 Java Object之间的转换,或者将将一种格式转化为另一种格式(譬如将应用数据转化成某种协议数据)
- Java 序列化:不推荐使用,占用空间大,也只有java语言能用
- Marshaling:比java序列化稍好
- XML :可读性好,但是占用空间大
- JSON :可读性也好,空间较小
- MessagePack :占用空间比JSON小,可读性不如JSON,但也还行
- Protobuf :性能高,体积小,但是可读性差
- hessian :跨语言、高效的二进制序列化协议,整体性能和protobuf差不多。
- 其他
String编解码
StringDecoder/StringEncoder
Protostuff编解码
protostuff是一个基于protobuf实现的序列化方法,它较于protobuf最明显的好处是,在几乎不损耗性能的情况下做到了不用我们写.proto文件来实现序列化
个人理解:protobuf是需要遵循proto语言进编译proto文件,protostuff是对他进一步包装
Protostuff项目地址链接
· Keepalive & idle监测 (心跳检测)keepalive
定时keepalive 消息,keepalive 消息与服务器正常消息交换完全不关联,定时就发送;
idel监测
Idle 监测,只是负责诊断,诊断后,做出不同的行为,决定Idle 监测的最终用途,一般用来配合keepalive ,减少keepalive 消息;
空闲监测+ 判定为Idle 时才发keepalive,有其他数据传输的时候,不发送keepalive ,无数据传输超过一定时间,判定为Idle,再发keepalive
二、网络IO
网络IO的过程,就是操作系统接收到网卡的数据,缓存到一个buffer中,然后应用程序调用操作系统的函数,从对应的buffer中取出数据。
[Client] [Server]
应用层 HTTP... 应用层
------------------- socket ------------------- socket
传输层 TCP ----- ---- 传输层
|==>操作系统:内核kernel/内核协议栈 ==>|
网络层 IP ----- ---- 网络层
链路层 链路层
物理层 物理层
连接建立:跟应用程序无关,是内核程序栈建立好连接创建一个socket,分配好对应的缓冲区资源,把连接放到连接队列里面,引用程序在连接队列将连接取走。
· IO概念I/O就是计算机内存与外部设备之间拷贝数据的过程
· IO类型 阻塞 / 非阻塞阻塞:没有数据传过来时,读会阻塞直到有数据;缓冲区满时,写操作也会阻塞。
非阻塞:非阻塞遇到这些情况,直接返回
同步:数据就绪后需要程序去读
异步:数据就绪后系统直接读好再回调给程序
个人理解:同步是应用层主动向操作系统要数据; 异步是操作系统主动通过回调函数给程序。
所以异步IO主要看操作系统支持成熟度
概述
BIO是blocking I/O的简称,它是同步阻塞型IO,其相关的类和接口在java.io下。
BIO模型简单来讲,就是服务端为每一个请求都分配一个线程进行处理,I/O操作都是基于流Stream的操作。
弊端
1.线程开销:客户端的并发数与后端的线程数成1:1的比例,线程的创建、销毁是非常消耗系统资源的,随着并发量增大,服务端性能将显著下降,甚至会发生线程堆栈溢出等操作;
2.线程阻塞:当连接创建后,如果该线程没有操作时,会进行阻塞操作,这样极大的浪费了服务器资源。
2.同步非阻塞IO非阻塞: 通过轮询去检测是否有数据准备好,没有数据准备好就一直轮询,直到有数据准备好则开始数据拷贝。
3.IO多路复用个人理解: 基于同步非阻塞IO进行优化,通过select/epoll等系统调用,仅遍历有事件的通道,而非遍历所有的通道
Java-NIO模型概述
NIO,称之为New IO或是non-block IO (非阻塞IO )
底层基于IO多路复用,IO多路复用时同步非阻塞,不过多加了一套检测机制;
检测机制核心思想:检测所有socket,哪些准备好了就操作,没准备好的就不操作
NIO的三大核心组件:Buffer(缓冲区)、Channel(通道)、Selector(选择器/多路复用器)
1.Buffer(缓冲区)
个人理解:Buffer作为数据容器
Buffer是一个对象,包含一些要写入或者读出的数据,在NIO中,所有数据都是用缓冲区处理的,读数据直接从缓冲区读,写数据直接写入到缓冲区,缓冲区的本质是一个数组,通常是一个字节数组(ByteBuffer),也可以使用其他类型,但缓冲区又不仅仅是一个数组,他还提供了对数据结构化访问以及维护读写位置等操作。
2.Channel(通道)
个人理解:Channel通道,上面有连接、读取、写入等事件操作
Channel是一个通道,管道,网络数据通过Channel读取和写入,Channel和流Stream的不同之处在于Channel是双向的,流只在一个方向上移动(InputStream/OutputStream),而Channel可以用于读写同时进行,即Channel是全双工的。
3.Selector(选择器/多路复用器)
个人理解:Selector作为注册轮询服务,轮询Channel的事件
Selector会不断轮询注册在其他的Channel,如果某个Channel上面发生读或者写事件,即该Channel处于就绪状态,他就会被Selector轮询出来,然后通过selectedKeys可以获取就绪Channel的集合,进行后续的I/O操作
安装一个signal处理函数,进程继续运行(不阻塞)。当数据准备好时,进程会收到一个SIGIO信号,可以在signal处理函数中调用IO操作(如read)处理数据。
5.异步IO基本流程
用户线程通过系统调用,告知kernel内核启动某个IO操作,用户线程返回。
kernel内核在整个IO操作完成后,通知用户进程,用户执行后续的业务操作。
重点在操作系统层面,目前还并不完善
三、Reactor线程模型 · 概念
Reactor线程模型是一种并发编程模型,具有指导意义的思想
Reactor模型中三种角色(Reactor、Acceptor、Handler):
Reactor
负责监听和分配事件,将I/O事件分派给对应的Handler。新的事件包含 连接建立就绪、读就绪、写就绪
Acceptor
处理客户端新连接,并分派请求到处理器链中
Handler
将自身与事件绑定,执行非阻塞读/写任务,完成channel的读入,完成处理业务逻辑后,负责将结果写出channel
· NIO下Reactor线程模型1.单Reactor单线程
所有的接收连接,处理数据的相关操作都是在一个线程中来完成,性能上有瓶颈
2.单Reactor多线程
把比较耗时的数据的编解码运算操作 放入线程池中来执行
3.主从Reactor多线程相较于单Reactor多线程,主从Reactor专门用线程去处理客户端连接接收工作
主Reactor(mainReactor)处理客户端连接接收工作;从Reactor(subReactor)处理读写编解码运算操作
这种模式也被叫做服务器的 1+M+N 线程模式
1:使用该模式开发的服务器包含一个(或多个,1 只是表示相对较少)连接建立线程
M:M 个 IO 线程
N:N 个业务处理线程。这是业界成熟的服务器程序设计模式。
优势
- MainReactor 线程与 SubReactor 线程的数据交互简单职责明确,MainReactor 线程只需要接收新连接,SubReactor 线程完成后续的业务处理。
- MainReactor 线程与 SubReactor 线程的数据交互简单, MainReactor 线程只需要把新连接传给SubReactor 线程,SubReactor 线程无需返回数据。
- 多个 SubReactor 线程能够应对更高的并发请求。
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
分享名称:【博学谷学习记录】超强总结,用心分享|架构师Netty框架学习总结-创新互联
分享URL:http://myzitong.com/article/copogo.html