Netty-架构与原理
概述
Netty是一个异步的基于事件驱动的网络框架。
NIO的概念是指:
- 没当客户端请求时,创建一个Socket Channel,并注册到Selector(多路复用器)上去。
- Selector关注服务器的IO读写事件,此时客户端不用等待IO事件完成,继续work
- 一旦服务器端完成IO操作,Selector会收到通知,同时告诉客户端已完成IO
- 客户端就可以通过Socket Channel获取相应的数据了
Selector并不是真正意义上的异步,是需要通过线程阻塞的方式监听IO事件变更,只是这种方式没有让客户端等待,是Selector在等待IO返回,并通知客户端去获取数据。
基于NIO
- Netty封装了NIO,不需要关注NIO的具体实现细节
- 对网络连接和访问做了处理
- 处理数据灵活,支持多种序列化框架
- 提供了线程池和Buffer机制
核心组件
- Channel:当客户端和服务器连接时会创建Channel,可以理解为Socket连接,负责基本的IO操作,如bind()、connect()、read()、write()
- EventLoop和EventLoopGroup:
2.1 消息的请求和响应组成了Event,如:连接已激活;数据读取;用户事件;异常事件;打开链接;关闭链接等
2.2 每个Channel都被分配给个EventLoop,一个EventLoop可以服务多个Channel,每个EventLoop占用一个Thread,同时这个Thread又会处理EventLoop上面发生的所有IO操作和事件
2.3 而EventLoopGroup就是用来生成EventLoop的,一个EventLoopGroup包含了多个EventLoop,EventLoopGroup创建一个新的Channel,并分配到EventLoop,在异步传输模式下,一个EventLoop可以处理多个Channel产生的事件,主要的工作是事件的发现和通知
2.4 相当于之前是一个Channel占用一个Thead,现在是多个Channel使用一个Thead。当客户端发送消息到服务端时,EventLoop告诉服务器去取消息同时客户端继续work,当服务器端发送消息到客户端,EventLoop通知客户端取消息。 - ChannelHandler,ChannelPipeline 和 ChannelHandlerContext,
3.1 ChannelHandler事件的处理者,在ChannelHandler中添加一些业务代码,如数据转换,业务处理等,针对入口数据和出口数据分别有不同的handler,ChannelInBoundHandler和ChannelOutBoundHandler
3.2 ChannelPipeline为ChannelHandler链提供容器
3.3 ChannelHandlerContext就是各个ChannelHandler之间的关联,ChannelHandlerContext参数贯穿ChannelPipeline
数据容器
Netty将ByteBuf作为数据容器,存放数据。
堆缓冲区
ByteBuf将数据存储在JVM,通过数组实现,可以快速访问,被JVM管理
直接缓冲区
堆外分配内存,不占用JVM,在Socket传输时性能好,直接从缓冲区发送数据
复合缓冲区
Netty提供了CompsiteByteBuf,可以将堆缓存和直接缓冲区数据放在一起使用更方便。
Bootstrap
Bootstrap的作用就是将Netty的核心组件配置到程序中,run起来。
分别有bootstrap和serverBootstrap
Bootstrap通过bind()绑定再通过connect()创建Channel
ServerBootstrap通过bind()之后创建ServerChannel
ServerBootstrap需要两个EventLoopGroup,第一组 ServerChannel 监听本地端口的套接字。第二组用来监听客户端请求的套接字。
粘包问题
TCP为了提高性能,会将需要发送的数据放到缓冲区,满了之后就发送给接收端,同样接收端也有缓冲区机制,来接收数据。
由于TCP本身是无界的数据流,本身是没办法避免的,只能在应用层协议控制:
- 使用定长消息,每次读完既定长度内容作为消息,FixedLengthFrameDecoder
- 使用带消息头协议、消息头存储标志位和长度,LengthFieldBasedFrameDecoder与LengthFieldPrepender
- 设置消息边界,比如在消息尾部设置换行符,LineBasedFrameDecoder与DelimiterBasedFrameDecoder
AIO
与NIO不同,当进行读写操作时,直接调用API的read、write。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。 即可以理解为,read/write方法都是异步的,完成后会主动调用回调函数。
Reactor
高性能网络编程模式
单线程Reactor
单线程模型就是一个reactor(select+循环),客户端(client)注册进来由reactor接收注册事件,然后再由reactor分发(dispatch)出去,由下面的处理器(read、decode、compute等)去处理
多线程Reactor
多线程Reactor中,注册接收事件都是由Reactor来做,其它的计算,编解码由一个线程池来做。单独一个线程接收请求,另一个线程池处理其它业务。这种模型的问题就是,使用线程池处理的时候,很多地方会有多次线程切换,上下文切换,导致效率比较低
Multiple Reactor
有多个Reactor,一个boss Reactor负责接收,另外几个worker Reactor负责读写,把业务部分的处理还是放到线程池中来做。
netty对三种Java基本的Reactor模式都是支持的
如果需要很复杂的计算,建议在handler中加入线程池进行处理。尽量不要让IO线程池阻塞。
https://mp.weixin.qq.com/s/UIZL78m105btP2HWFmQmlw
https://www.cnblogs.com/guos/p/12188495.html