Netty的介绍
介绍:
Netty 是JBoss提供的一个java开源框架,Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可用性的网络服务器和客户端程序。也就是说 Netty 是一个基于
Nio
的编程框架,使用Netty可以快速的开发出一个网络应用。 并且可以在不妥协的情况下实现易于开发、性能、稳定性和灵活性。
.
.
Netty核心概念的介绍
.
1. Bootstrap、ServerBootstrap
介绍:
Bootstrap
的意思是引导,其主要作用是配置整个Netty程序,将各个组件整合起来。ServerBootstrap
是服务器端的引导类。Bootstrap
是客户端端的引导类。
- Bootstrap用于连接远程主机它有一个
EventLoopGroup
- ServerBootstrap用于监听本地端口有两个
EventLoopGroup
.
2. EventLoopGroup
介绍:
EventLoopGroup 主要是管理
EventLoop
的生命周期,可以将其看作是一个线程池,其内部维护了一组EventLoop
,每个EventLoop
对应处理多个Channel
,而一个Channel
只能对应一个EventLoop
。补充:
其中
EventLoop
维护了一个线程和任务队列,支持异步提交执行任务 。
.
3. Channel(数据传输流通道)
介绍:
与
WebSocket
进行I/O
操作(例如读取,写入,连接和绑定)的组件的枢纽,Channel表示可以理解为一个连接,可以理解为每一个请求,就是一个Channel
主要实现:
FileChannel
: 文件通道,用于文件的读和写DatagramChannel
: 用于 UDP 连接的接收和发送SocketChannel
: 把它理解为 TCP 连接通道,简单理解就是TCP 客户端ServerSocketChannel
: TCP对应的服务端,用于监听某个端口进来的请求主要类:
1. ChannelHandler:
介绍:
业务逻辑处理器,用于处理业务请求与响应,分为
ChannelInboundHandler
和ChannelOutboundHandler
1. ChannelInboundHandler:
作用: 处理进站数据和所有状态更改事件(进站指的是读操作等由通道引发的事件)
2. ChannelOutboundHandler:
作用: 处理出站数据,允许拦截各种操作(出站指的是写操作等由用户触发的事件,发送到远方服务器的事件)
2. ChannelPipeline:
介绍:
ChannelPipeline
其实就是一个ChannelHandler
容器,里面包括一系列的ChannelHandler
实例,用于拦截流经一个Channel
的入站和出站事件,主要用于保存处理过程需要用到的ChannelHandler
和ChannelHandlerContext
3. ChannelHandlerContext:
介绍:
允许与其关联的
ChannelHandler
与它相关联的ChannlePipeline
和其它ChannelHandler
来进行交互。它可以通知相同ChannelPipeline
中的下一个ChannelHandler
,也可以对其所属的ChannelPipeline
进行动态修改。用于传输业务数据4. ChannelContext:
介绍:
Channel(通信管道)的上下文
交互流程:
- 事件传递给
ChannelPipeline
的第一个ChannelHandler
ChannelHandler
通过关联的ChannelHandlerContext
传递事件给ChannelPipeline
中的 下一个ChannelHandler
通过关联的ChannelHandlerContext
传递事件给ChannelPipeline
中的 下一个
.
4. Futrue、ChannelFuture
介绍:
Future提供了另一种在操作完成时通知应用程序的方式。可以看作是一个异步操作结果的
占位符
;它将在未来的某个时刻完成,并提供对其结果的访问。补充:
Netty的每一个出站操作都会返回一个
ChannelFuture
。 Future上面可以注册一个监听器,当对应的事件发生后会出发该监听器。
.
5. ByteBuf(储字节的容器)
介绍:
ByteBuf
是一个存储字节的容器,最大特点就是使用方便,它既有自己的读索引和写索引,方便你对整段字节缓存进行读写,也支持get/set
,方便你对其中每一个字节进行读写,他的数据结构如下图所示:
ByteBuf的模式:
1. Heap Buffer(堆缓冲区)
堆缓冲区是ByteBuf最常用的模式,他将数据存储在堆空间
2. Direct Buffer(直接缓冲区)
直接缓冲区是ByteBuf的另外一种常用模式,他的内存分配都不发生在堆,
jdk1.4
引入的nio
的ByteBuffer
类允许jvm
通过本地方法调用分配内存。这样做有两个好处如下:
通过免去中间交换的内存拷贝, 提升IO处理速度; 直接缓冲区的内容可以驻留在垃圾回收扫描的堆区以外
DirectBuffer 在
-XX:MaxDirectMemorySize=xxM
大小限制下, 使用 Heap 之外的内存,GC
对此”无能为力”,也就意味着规避了在高负载下频繁的GC过程对应用线程的中断影响3. Composite Buffer(复合缓冲区)
复合缓冲区相当于多个不同ByteBuf的视图,这是netty提供的,jdk不提供这样的功能
.
6. Decoder(解码器)
介绍:
负责将消息从字节或其他序列形式转成指定的消息对象;类型有:1. 解码字节到消息。2.解码消息到消息
主要类:
1. ByteToMessageDecoder抽象类
作用: 将字节消息解码成
Object
对象主要方法:
1.
decode(ChannelHandlerContext, ByteBuf, List<Object>)
方法作用: 将
ByteBuf
数据解码成其他形式的数据。2.
decodeLast(ChannelHandlerContext, ByteBuf, List<Object>)
方法实际上调用的是
decode(...)
主要流程:
2. MessageToMessageDecoder抽象类
作用: 将消息对象转成消息对象
主要方法:
1.
decode(ChannelHandlerContext, ByteBuf, List<Object>)
方法作用: 将
ByteBuf
数据解码成其他形式的数据。主要流程:
Netty中常用的自带解码器:
- DelimiterBasedFrameDecoder: 分隔符解码器,以设定的符号作为消息的结束解决粘包问题
- FixedLengthFrameDecoder: 定长解码器,作用于定长的消息
- LineBasedFrameDecoder: 按照每一行进行分割,也就是特殊的分隔符解码器,它的分割符为
\n
或者\r\n
。- LengthFieldBasedFrameDecoder: 通过消息中设置的长度字段来进行粘包处理。该解码器总共有5个参数
int maxFrameLength
: 单个包的最大大小int lengthFieldOffset
: 定义长度的字段的相对包开始的偏移量int lengthFieldLength
: 定义长度字段所占字节数int lengthAdjustment
: 数据长度字段之后剩下包的字节数 - 数据长度取值(也就是长度字段之后的所有非数据的其他信息)int initialBytesToStrip
: 从包头开始,要忽略的字节数
HttpRequestDecoder
: 将字节解码为HttpRequest
、HttpContent
和LastHttpContent
消息HttpResponseDecoder
: 将字节解码为HttpResponse
、HttpContent
和LastHttpContent
消息HttpObjectAggregator
: 将http消息的多个部分聚合起来形成一个FullHttpRequest
或者FullHttpResponse
消息
.
7. Encoder(编码器)
介绍:
将消息对象转成字节或其他序列形式在网络上传输;类型有:1. 消息对象编码成消息对象。2.消息对象编码成字节码主要类:
1. MessageToByteEncoder抽象类
作用: 与
ByteToMessageDecoder抽象类
相反主要流程:
2. MessageToMessageEncoder抽象类
作用: 与
MessageToMessageDecoder抽象类
相反主要流程:
.
.
使用Netty搭建安卓Socket客户端的使用
.
1. 在 build.gradle
添加Netty的依赖
dependencies {
implementation 'io.netty:netty-all:4.1.23.Final'
}
.
2. 使用Netty的简单使用
2.1. 创建ConnectionWatchdog类
//创建接口
interface ChannelHandlerHolder {
fun handlers(): Array<ChannelHandler>
}
//创建接口
interface ReconnectSuccessHolder {
fun reconnectSuccess(channel: SocketChannel?)
}
/**
* 连接监控器
* */
@ChannelHandler.Sharable
abstract class ConnectionWatchdog(
val bootstrap: Bootstrap,
val timer: Timer,
val port: Int,
val host: String,
val reconnect: Boolean
) : ChannelInboundHandlerAdapter(), TimerTask, ChannelHandlerHolder, ReconnectSuccessHolder {
private var attempts: Int = 0
//TimerTask接口的方法
override fun run(p0: Timeout?) {
//bootstrap已经初始化好了,只需要将handler填入就可以了
val future: ChannelFuture = synchronized(bootstrap) {
bootstrap.handler(object : ChannelInitializer<Channel>() {
@Throws(Exception::class)
override fun initChannel(ch: Channel) {
ch.pipeline()
.addLast(*handlers())
}
})
bootstrap.connect(host, port)
}
//future对象
future.addListener {
f ->
val succeed = f.isSuccess
//如果重连失败,则调用ChannelInactive方法,再次出发重连事件,一直尝试12次,如果失败则不再重连
if (!succeed) {
Log.i("netty", "重连失败")
(f as ChannelFuture).channel().pipeline().fireChannelInactive()
} else {
val socketChannel = future.channel() as SocketChannel
reconnectSuccess(socketChannel)
Log.i("netty", "重连成功")
}
}
}
override fun channelInactive(ctx: ChannelHandlerContext?) {
super.channelInactive(ctx)
Log.i("netty", "链接关闭")
if(reconnect){
Log.i("netty", "链接关闭,将进行重连")
//重连的间隔时间会越来越长
timer.newTimeout(this, 10, TimeUnit.SECONDS)
}
ctx!!.fireChannelInactive()
}
override fun channelActive(ctx: ChannelHandlerContext?) {
super.channelActive(ctx)
Log.i("netty", "当前链路已经激活了,重连尝试次数重新置为0")
attempts = 0
ctx!!.fireChannelActive()
}
}
.
**2.2. ConnectorIdleStateTrigger**
/**
* 连接空闲处理
* */