这张脑图整理自《netty实战》

其中书中的知识点都有覆盖到,书中的netty使用案例没有整理出来。

用脑图构建netty知识脉络_数据

Netty实战
Netty是什么?
是一款用于创建高性能网络应用程序的高级框架
异步的
事件驱动的
主要构件
Channel
Java NIO的基本构造
数据传入或传出的载体
可以打开、关闭
可以连接、断开连接
Netty内部为每个Channel分配了一个EventLoop
回调
一个方法
这个方法的引用提供给另一个方法,以便后者在适当的时机调用前者
作用
通常在操作完成后通知相关方
Netty使用回调处理事件
Future
同样可用作通知
方法1调用方法2,方法2将耗时操作交给线程处理,并立刻返回一个Future占位符,方法1可以继续干活,并在将来使用Future得到方法2交给线程处理后的真正执行结果
弊端
get()之前需要手动检测是否完成
或者直接get(),这将会一直阻塞到真正的操作完成
Netty的改进
ChannelFuture
增加了监听器,在真正操作完成时,自动获得通知
ChannelFutureListener
operationComplete()
Future和回调相辅相成,通常一起使用
事件和ChannelHandler
事件可触发的动作
记录日志
数据转换
流控制
应用程序逻辑
......
入站事件
连接激活或失活
数据读取
用户事件
错误事件
......
出站事件
将数据写到或者冲刷套接字
打开或关闭远程节点的连接
......
ChannelHandler可看作是为相应特定事件而被执行的回调
一组ChannelHandler事件处理器构成一条链
事件->处理器->事件->处理器->->->->
Netty提供了大量预定义的ChannelHandler实现
HTTP
SSL/TLS
......
Netty的传输方式
NIO
基于java.nio.channels包,基于选择器的方式
零拷贝
数据直接从文件系统移动到网络接口
而不需要从内核空间复制到用户空间
Epoll
仅Linux支持
比NIO传输更快,完全非阻塞
支持零拷贝
OIO
基于java.net (http://java.net)包,使用阻塞流
SO_TIMEOUT超时抛出异常,从而继续处理循环
Local
在VM内部通过管道进行通信的本地传输
不需要暴露网络
Embedded
常用于单元测试ChannelHandler
不需要真实的基于网络的传输,可用于测试ChannelHandler
可以将一组ChannelHandler嵌入到其他的ChannelHandler内部,从而扩展一个CH的功能,而不需要修改其内部代码
ByteBuf
是什么?
Netty的数据容器
作为JavaNIO提供的ByteBuffer替代品
功能更多
使用更加简单方便
优点
可被用户自定义的缓冲区类型扩展
通过内置的复合缓冲区类型实现了透明的零拷贝
容量可按需增长
读和写切换不需要调用ByteBuffer的flip()方法
读和写使用不同的索引
试图读超出写入的长度数据,将会溢出异常
可以指定ByteBuf的最大容量,默认时Integer.MAX_VALUE
写索引超出也会溢出异常
支持方法的链式调用
支持引用计数
支持池化
使用模式
堆缓冲区(支撑数组)
适合有遗留数据需要处理的情况
存储在JVM的堆空间中
提供快速的分配和释放
直接缓冲区
不被GC回收
分配和释放较为昂贵
多一次复制到堆
复合缓冲区
CompositeByteBuf
提供一个聚合视图,方便管理
字节级操作
随机访问索引
getByte(i)
顺序访问索引
可丢弃字节
0~readIndex
read后即可丢弃
write会增加writeIndex
get不会增加readIndex
set不会增加writeIndex
丢弃后,readIndex变为0,writeIndex也会变少
可读字节
buffer.isReadable()
可写字节
buffer.writableBytes()
索引管理
mark
reset
clear
同时清零
查找操作
int index = buffer.forEachByte(ByteBufProcessor.FIND_CR);
FIND_CR
FIND_NULL
查找值的索引
派生缓冲区
返回新的ByteBuf实例
但共享了原始ByteBuffer
因此创建成本低廉
但改变了派生缓冲区,也将改变原始缓冲区
派生方法
duplicate()
slice()
slice(int,int)
Unpooled.unmodifiableBuffer(...)
order(ByteOrder)
readSlice(int)
非派生方法
copy()
产生了独立的真实缓冲区的副本
读/写操作
write\read
改变index
set\get
不改变index
更多的操作
capacity()
返回当前可容纳的字节数
可扩展
maxCapacity()
返回最大可容纳的字节数
ByteBufHolder
用于缓冲区池化,从池中借用ByteBuf
在需要时自动释放
三个主要方法
content()
返回该ByteBufHolder所持有的ByteBuf
copy()
返回这个ByteBufHolder的一个深拷贝,包括一个其所包含的ByteBuf的非共享拷贝
duplicate()
返回这个ByteBufHolder的一个浅拷贝,包括一个其所包含的ByteBuf的共享拷贝
ByteBuf分配
ByteBufAllocator
池化的
按需分配
支持操作
buffer()
返回一个基于堆或者直接内存存储的ByteBuf
heapBuffer()
返回一个基于堆内存存储的ByteBuf
directBuffer()
返回一个基于直接内存存储的ByteBuf
compositeBuffer()
compositeHeapBuffer()
compositeDirectBuffer()
ioBuffer()
返回一个用于套接字的I/O操作的 ByteBuf
Unpooled缓冲区
未池化的
支持操作
buffer()
未池化的基于堆
directBuffer()
未池化的基于直接内存
wrappedBuffer()
包装了给定数据的
copiedBuffer()
复制了给定数据的
ByteBufUtil
最常用方法
hexdump()
十六进制打印ByteBuf
equals()
比较ByteBuf实例
引用计数
对象的活动引用的数量
引用数量为0的实例会被释放
池化实例释放的依据
buffer.release()
返回该对象是否被释放
ChannelHandler和ChannelPipeline
Channel状态
已创建,未注册到EventLoop
ChannelUnregistered
已注册到EventLoop
ChannelRegistered
处于活动状态(已连接到它的远程节点),可收发数据了
ChannelActive
没有连接到远程节点
ChannelInactive
ChannelHandler的生命周期
被添加到ChannelPipeLine中
handlerAdded
从ChannelPipeLine中移除
handlerRemoved
在ChannelPipeLine中发生异常
exceptionCaught
ChannelInboundHandler接口
channelRead
从channel读数据时调用
channelComplite
读完成时调用
channelWritability
channel可写状态改变时调用
ChannelOutboundHandler接口
bind()
将channel绑定到本地地址
connect()
将channel连接到远程节点
disconnect()
请求将channel从远程节点中断开
close()
请求关闭channel时调用
deregister()
请求将channel从EventLoop中注销时调用
read()
读数据时
flush()
通过channel将入队数据冲刷到远程节点
write()
通过channel将数据写到远程节点
ChannelHandler适配器
ChannelInboundHandlerAdapter
SimpleChannelInboundHandler<T>
ChannelOutboundHandlerAdapter
资源泄漏保护
ResourceLeakDetector
泄漏检测级别
DISABLED
SIMPLE
ADVANCED
PARANOID
消耗大,只在调试阶段使用
ChannelPipeline
管理操作
add*()
入站在头部
入站时经过
ChannelInboundHandler
1->2->3->...
而不会经过ChannelOutboundHandler
出站在尾部
出站时经过
ChannelOutboundHandler
6->5->4->...
而不会经过ChannelInboundHandler
remove()
replace()
访问操作
get()
通过名称或者类型返回ChannelHandler
context()
返回和 ChannelHandler绑定的ChannelHandlerContext
names()
返回所有ChannelHandler的名称
触发事件
入站操作
fire*()
出站操作
writeAndFlush()
......
ChannelHandlerContext
每向pipline中添加一个channelhandler就会创建一个context
调用方法时,只会传播给此context当前关联的ChannelHandler的下一个处理该事件的ChannelHandler
而Channel和pipeline是会沿着整个pipeline传播
具有丰富的处理事件和执行IO操作的API
可让ChannelHandler与它的pipeline以及其他ChannelHandler交互
操作
pipeline()
writeAndFlush()
name()
handler()
......
意义
减少将事件传经对它不感兴趣的ChannelHandler所带来的开销
避免将事件传经那些可能会对它感兴趣的ChannelHandler
高级应用
动态切换协议
异常处理
处理入站异常
exceptionCaught
默认转发给pipeline的下一个handler,直到最后没被处理的话会被标记为未处理
可重写处理异常,如不往下抛异常等
处理出站异常
基于通知机制
每个出站操作都将返回一个ChannelFuture
ChannelPromise是ChannelFuture的一个子类
几乎每一个出站方法都传入了一个ChannelPromise
可被分配用于异步通知的监听器
也可以提供立即通知的可写方法
setSuccess()
setFailure(Throwable cause)
addListener(ChannelFutureListener)的时机
方法1
在每次write后返回的future中添加
优点
可共享调用者的属性和方法等
方法2
在ChannelHandler的write方法中,统一添加
优点
一次编写,到处可用
EvenLoop和线程模型
线程模型概述
使用线程池
从池中得到一个空闲的Thread去执行一个Runnable实例任务
用完后放回池中
减少线程创建和销毁开销
EventLoop
while(!terminated)
List<Runnable> readyEvents = blockUntilEventsReady();
阻塞,直到有事件已经就绪可被运行
for (Runnable ev: readyEvents) {
   ev.run();
}
循环遍历,并处理所有的事件
一个EventLoop可用于服务多个Channel
优化
任务调度
ScheduledExecutorService
延迟之后运行或者周期性地执行任务
schedule()
scheduleAtFixedRate()
取消任务
future.cancel()
线程池方式
单线程方式
实现细节
如果是EventLoop中的事件,则立即执行
否则加入队列,提交到线程池执行
EventLoop线程分配
非阻塞
一个EventLoop占用一个线程
一个线程管理多个Channel
阻塞
一个EventLoop占用一个线程
一个线程管理一个Cahnnel
Bootstrap引导
是什么?
如何对应用程序进行配置,将各个部分组织起来,让应用程序运行起来的过程
两种引导
BootStrap
为客户端和使用无连接协议的应用程序创建Channel
bind()用于无连接协议(UDP)
DatagramChannel
用于收发数据包
无法得知发送的数据是否被接收成功
ServerBootStrap
方法
handler()
此方法添加的ChannelHandler由ServerChannel(此为用于接受子Channel的Channel)处理
bind()创建了ServerChannel
ServerChannel管理了很多子连接的Channel
childHandler()
此方法添加的ChannelHandler由已被接受的子Channel处理
更常用
option()
用于配置Channel-Option,通过bind()方法设置到Channel
如配置5秒连接超时
bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS,5000)

SO_TIMEOUT 只对于 OIO有效,对于NIO 没有作用,用于超时控制
bind()后修改option无效
childOption()
更常用
attr()
用于指定ServerChannel上的属性,方便使用
childAttr()
更常用
一些常见应用
在子Channel中引导BootStrap客户端

通过共享Channel的EventLoop来避免新增EventLoop线程产生资源浪费
bootstrap.group(channel.eventLoop())
在引导过程中添加多个ChannelHandler
ChannelInitializerImpl extends ChannelInitializer
重写initChannel()
在initChannel()中添加ChannelHandler
channel.pipeline().addLast(channel handler)
serverbootstrap.childHandler(new ChannelInitializerImpl())
优雅的关闭应用程序
释放所有的资源,并且关闭所有的当前正在使用中的Channel
group.shutdownGracefully().sync();
EventLoop和Chanel不能混用NIO和OIO
网络协议
WebSocket
新版浏览器基本都支持WebSocket,WS是HTML5的一部分
可通过HTTP协议进行WebSocket协议握手
在协议升级后即可通过WebSocket传输消息
特点
双向
异步
实时
应用
WEB版聊天室
......
TCP
优点
有序
安全
缺点
较UDP慢
UDP
缺点
无序,可能丢包
不安全
优点
传输快
无需握手确认
无需反馈
支持多播
传输到一个预定义的主机组
支持广播
传输到网络(或子网)上的所有主机
成员
广播者
Broadcaster
监听者
Monitor
应用
日志广播
视频传输
......