主要构件
-
Channel: 抽象一个到实体(硬件、文件、Socket)的连接,封装了不同的I/O操作,屏蔽底层细节。用于执行所有的I/O操作。
-
ChannelHandler:事件处理器
- ChannelInboundHandler:处理入站事件(输入)
- ChannelOutboundHandler:处理出战事件(输出)
-
ChannelPipeline: 包含一组事件处理器 – 处理某个Channel的I/O事件的流程
- ChannelHandler的容器(有序表)
- 每个Channel持有一个ChannelPipeline,其I/O事件由按顺序由ChannelPipeline内的ChannelHandler处理
-
EventLoop:事件循环–处理其负责的Channel的所有I/O和事件
-
EventLoopGroup:EventLoop池
-
Bootstrap : 启动网络处理
- Bootstrap:引导客户端,连接远程主机
- ServerBootstrap: 引导服务器,监听一个端口,接收连接并创建Channel进行通信
-
ChannelFuture:异步通知
- 由异步操作返回,代表一个未完成的操作
- 可向其添加监听器addListener(),以在操作完成时做出动作
-
ChannelPromise:继承自ChannelFuture,可写的ChannelFuture
Channel
- 抽象一个到实体(硬件、文件、Socket)的连接,封装了不同的I/O操作,屏蔽底层细节。提供统一的I/O操作。
- Channel被创建时,将被分配一个ChannelPipeline(包含用于处理其一切I/O数据和事件的Handler)和ChannelConfig(描述其配置)
- 为线程安全的
- 实现类(内置的传输)
- 基于NIO的传输(位于io.netty.channel.socket.nio包)–基于java.nio.channels包
- NioServerSocketChannel 用于监听并接受(accept)连接
- NioSocketChannel TCP通信
- NioDatagramChannel UDP通信,收发报文
- 基于Epoll的传输(位于io.netty.channel.epoll包)-- JNI 驱动的 epoll()。Linux下高负载时由于NIO。
- 基于OIO的传输–基于java.net包,阻塞I/O
- Local–通过JVM内部通道,进行本地通信
- Embedded–用于测试,提供操作出入站数据的接口。
- 基于NIO的传输(位于io.netty.channel.socket.nio包)–基于java.nio.channels包
- 生命周期:包含4个状态,在发送变化时触发响应的事件。
- ChannelUnregistered :未注册到 EventLoop
- ChannelRegistered :被注册到了 EventLoop
- ChannelActive :处于活动状态,可进行传输
- ChannelInactive:不可进行传输
EventLoop & EventLoopGroup
-
EventLoop处理某个Channel的I/O和事件的整体流程(监听I/O,分配事件)
-
一个EventLoop在生命周期内之和一个Thread绑定(只有一个线程)
-
使用非阻塞I/O时,一个EventLoop处理一个或多个Channel的I/O和事件
-
使用阻塞I/O时,一个EventLoop处理一个Channel
-
EventLoopGroup即EventLoop的池,相当于一个线程池
-
任务(Runnable、Callable)可以提交给EventLoop进行调度执行(
- schedule方法:指定时间后执行
- scheduleAtFixRate方法:以指定周期调用
-
事件和任务是以先进先出(FIFO)的顺序执行的,以保证数据顺序的正确
-
继承关系:
ChannelHandler
-
ChannelInboundHandler:处理入站事件(输入)
-
ChannelOutboundHandler:处理出战事件(输出)
-
适配器-ChannelInboundHandlerAdapter&ChannelOutboundHandlerAdapter
- 提供基本实现:将事件直接传入下一个Hundler
- 实现一个Handler时不必将其方法全部实现一遍
-
共享的ChannelHandler:在多个ChannelPipeline内安装同一个ChannelHandler对象(可用于跨连接收集数据),需将该ChannelHandler类声明为可以共享的(使用注解**@Sharable**)
-
当某个 ChannelInboundHandler 的实现重写 **channelRead()**方法时
- 要么将消息传递给下一个Handler
- 要么负责显式地释放相关的池化的ByteBuf(可使用ReferenceCountUtil.release(…))
-
异常处理
- 入站过程的异常将流过ChannelPipeline,直到被某个ChannelInboundHandler的exceptionCaught方法捕获。
- 出站过程:ChannelOutboundHandler的大多数方具有ChannelPromise参数。通过setFailure(Throwable cause)方法通知操作失败,并携带异常
ChannelHandlerContext
- 一个ChannelHandlerContext对象代表ChannelHandler和ChannelPipeline之间的关联
- ChannelHandler插入ChannelPipeline时创建
- ChannelHandler在生命周期内一直与该ChannelHandlerContext绑定
- ChannelHandler与在同一个Pipeline中相邻Handler的交互(事件的向下传递)实际上是通过Context对象进行
- 在ChannelHandlerContext上调用write(),数据将从尾端经过所有ChannelOutboundHandler到头部,传入Channel。
- 在ChannelHandlerContext上调用read(),从Channel读取数据(按正常流程执行,通过Pipeline)
Bootstrap
-
配置并运行服务器/客户端
-
-
Bootstrap类,引导客户端
- 负责为客户端和使用无连接协议的应用程序创建 Channel,与服务器通信
- 无连接的客户端使用Bootstrap的bind() ,创建一个Channel绑定指定端口收发UDP包
- 有连接的客户端使用Bootstrap的connect(),连接到远程主机
-
ServerBootstrap类,引导服务器
- 负责使用一个父Channel监听并接受来自客户端的连接,创建子Channel与客户端进行通信
- 调用bind(),创建一个ServerChannel用于监听并接受来自客户端的连接
- 收到来自客户端的连接后,创建一个Channel来负责该连接
ByteBuf
Netty交换和处理数据的容器。替代Java的ByteBuffer。
-
维护两个索引: 读索引readerIndex、写索引writerIndex
-
readXXX() 将推进readerIndex; writeXXX()将推进writerIndex. (getXXX(), putXXX()不会改变索引)
-
有效的字节在 [readerIndex, writerIndex) 上
-
-
调用discardReadBytes() 回收可丢弃字节(极有可能导致内存复制)
-
-
-
数据存储方式
- 支撑数组(backing array) : 将数据储存在JVM堆空间中,即使用一个Java数组。
- buf.hasArray() 判断是否有支撑数组
- buf.array() 获取其数组
- 直接缓冲区: 通过本地调用获取内存,直接向操作系统申请的内存。不会被垃圾回收。如果数据不在直接缓冲区,通过socket发送数据时,会将JVM堆中的数据复制到直接缓冲区,使用直接缓冲区更快。
- 支撑数组(backing array) : 将数据储存在JVM堆空间中,即使用一个Java数组。
-
复合缓冲区 通过ByteBuf的子类CompositeByteBuf创建两个缓冲区的组合视图。
//两个ByteBuf组合为一个ByteBuf,不会发生拷贝 ByteBuf b = Unpooled.wrappedBuffer(b1,b2);
-
创建缓冲区:
- 使用ByteBufAllocator创建ByteBuf。
- 可通过**Channel或ChannelHandlerContext的alloc()**获取ByteBufAllocator实例。
- ByteBufAllocator内置实现:
- PooledByteBufAllocator: 池化ByteBuf
- UnpooledByteBufAllocator: 不进行池化
- 默认(4.1.x)使用PooledByteBufAllocator(引导服务器时可配置)
- 可使用Unpooled工具类创建未池化的ByteBuf
ByteBuf b = Unpooled.buffer(n);
-
自动扩容 - 超过初始大小时,自动扩容。
-
引用计数
- ByteBuf使用引用计数管理资源
- buf.realse() 计数减1
- buf.retain() 计数加1
编解码器框架 (编解码器框架–内置的一些列ChannelHandler)![请添加图片描述](https://i-blog.csdnimg.cn/blog_migrate/803f2354a8e5bc9896f3755f1793687a.png)
- 解码器 Decoder
- ByteToMessageDecoder : 从Channel读取的数据–>程序内部的数据格式
- MessageToMessageDecoder :将消息由T类型转换其他类型(转换两个对象的InboundHandler)
- ReplyingDecoder:便于积累数据到足够长度后进行转换
- TooLongFrameException 数据长度超过解码器设定的限制,抛出该异常
- 编码器 Encoder
- MessageToByteEncoder : 程序内部的数据格式 --> 适合传输的数据 (由Channel传出)
- MessageToMessageEncoder :将消息由T类型转换其他类型(转换两个对象的OutboundHandler)
- 编解码器 Codec – 将编码器 和 解码器 组合到一个类中
- ChannelDuplexHandler :同时实现InboundHandler和OutboundHandler的Adapter
常用预置编解、码器
对HTTP(S)的支持
Netty中HTTP相关的类:
- 编解码Http请求和响应
- HttpRequestEncoder
- HttpRequestDecoder
- HttpResponseEncoder
- HttpResponseDecoder
- HttpClientCodec (HttpRequestEncoder + HttpResponseDecoder)
- HttpServerCodec (HttpRequestDecoder + HttpResponseEncoder)
- 聚合HTTP消息
- HttpObjectAggregator 将HttpMessage跟HttpContent聚合为FullHttpRequest或FullHttpResponse
- HTTP压缩
- HttpContentDecompressor 客户端用于解压响应
- HttpContentCompressor 服务器端进行压缩
- HTTPS
- SslHandler (一般作为第一个Handler加入)
- WebSocket
- WebSocketServerProtocolHandler(“/websocket”) 处理WebSocket协议,以"/websocket"为进行升级握手的URI
- TextFrameHandler() 处理WebSocket的Text帧
- …
其他
-
处理空闲
- IdleStateHandler(…) 空闲(既没有受到消息,也没有发出消息)超过指定时间,触发IdleStateEvent事件。
- ReadTimeoutHandler(…) 在指定时间间隔内未受到消息,触发ReadTimeoutException
- WriteTimeoutHandler(…) 在指定时间间隔内未发出消息,触发WriteTimeoutException
-
使用分隔符分割
- DelimiterBasedFrameDecoder(分割符) 按指定分隔符,分割
- LineBasedFrameDecoder 按’\n’ 或 '\r\n’分割
-
基于长度的分割
- FixedLengthFrameDecoder 按指定长度分割
- LengthFieldBasedFrameDecoder 根据帧头部的长度进行分割
-
序列化
- JDK序列化
- CompatibleObjectDecoder
- CompatibleObjectEncoder
- ObjectDecoder
- ObjectEncoder
- JDK序列化
传输大文件
- 使用FileRegion进行零拷贝的传输。(实现类: DefaultFileRegion)
- 需要将文件内容复制到用户内存再传输时,使用ChunkedInput(需在Pipeline中添加 ChunkedWriteHandler),可避免大量内存消耗。