.1. Netty-learn
.1.1. Gerneral
.1.1.1. Nio buffer
- capacity 总数量
- limit 用于限制读写
- position 下一个读写位置
- https://www.bilibili.com/video/av33707223?p=37 节 去掉buffer.clear()注释掉后分析
- position<limit<capacity
.1.1.2. Nio网络基本流程
- selector 办事大厅
- 获取ServerSocketChannel
- 设置ServerSocketChannel 属性(是否阻塞)
- 获取ServerSocket
- 给ServerSocket绑定端口
- 将通道注册到selector
- 循环selecor
- 获取selectorKey
- 判断selectorKey类型返回channel
- 处理
- 移除selectorKey
.1.1.3. Zero copy
- 缺少由user model缓存到system kernel缓存;由system kernel缓存到user model缓存下内容拷贝2次 (用户态优化)
- System kernel下kernel buffer到socket buffer内容拷贝改为文件描述符拷贝
- FileChannel transferto transferFrom 使用零拷贝
.1.1.4. Reactor设计模式
- Handle(句柄和描述符,产生事件):本质表示一种资源,该资源(如何写入,写出)可表示一个个事件。
- Synchronous Evnet demultiplexer(同步事件分离器):它本身是一个系统调用,用于等待事件的发生(事件可能一个,多个),调用方在调用它的时候会被阻塞,一直阻塞到同步事件分离器上有事件产生为止,位于linux来说,同步事件分离器就是常用的I/O多路复用机制,比如说select,poll,epoll等,在Java NIO中,同步事件分离器对于组件selector,对于的阻塞方法select。
- Event Handler(事件处理器):本身由多个回调方法构成,这些回调方法构成了与应用相关的对于某个事件的反馈机制。java NIO中没有,netty中有(比如SimpleChannelInBand,SimpleChanneloutBand)
- Concrete Event Handler(具体事件处理器):它是事件处理器的实现 ,从而实现特定的逻辑(比如自己写的MyHandler)
- Initation Dispatcher(初始分发器):实际上就是Reactor角色,它本身定义了一些规范,这些规范用于控制事件的调度方式,同时又是提供了应用进行事件处理器的注册,删除等,它本身是整个事件处理器的核心所在,initiaion dispathcer会通过同步事件分离器来等待事件的发生,一旦事件发生,Initation Dispatcher首先会分离出每一个事件,然后调用事件处理器,最后调用相关的回调函数来处理这些事件。
.1.1.5. NioEventLoopGroup
- NioEventLoopGroup extends MultithreadEventLoopGroup ,初始化时需要传入线程数(defalt=Math.max(1, SystemPropertyUtil.getInt(
“io.netty.eventLoopThreads”, Runtime.getRuntime().availableProcessors() * 2)))
- 线程数大小极为EventExecutor 数组大小
- EventLoopGoup包含多个EventLoop
- 一个EventLoop在他整个生命周期中都只会与唯一的一个Thread进行绑定
- 所有的EventLoop所处理的各种I/O事件都将在它所关联的Thread上处理
- 一个channel在它的整个生命周期中只会注册在一个EventLoop上
- 一个EventLoop在运行过程中,会被分配给一个或多个channel(多个channel会被注册到一个Selector上)
- handler中业务是单线程执行,不需要并发控制,因此不能做耗时操作
- Channel的实现一定是线程安全的,因此,我们可以存储一个channel 引用,并且在需要向远端发送数据时,通过这个引用来调用channel相关的方法,即使当时有很多线程都在使用它也不会出现多线程问题,而且。消息一定会按照顺序发送出去
- 在业务开发中,不要将长时间执行的耗时任务放置到EventLoop的执行队列中,因此它会一直阻塞该线程所对于的所有channel上的其他执行任务,如果我们需要阻塞调用或耗时操作,那么我们需要使用一个专门的EventExcutor(业务线程池)
- 使用 java eventExcutor
- 借助pipeline中添加channelHandler调用的addLast(group , handler)
.1.1.6. Netty 组件间关系
- main Reactor 和 sub Reactor分别对于netty 中的bossGroup 和workGroup,selector 对于EventLoop
- ChannelInitializer(采用模板模式) 继承 ChannelInboundHandlerAdapter 继承 ChannelHandlerAdapter并且实现了ChannelInboundHandler接口,这里采用适配器模式(将ChannelHandlerContext(两空) 转化成ChannelInboundHandler(三空)接口)。以上是ChannelHandler接口转换
- ChannelPipline 中存放ChannelHandler,包含InBoundHandler和OutBoundHandler
- Channel中可以获取其对于的ChannelPipline ,ChannelPipline 也关联着唯一的Channel
- ChannelHandlerContext 何以获取channel ChannelHandler ChannelPipline ,自身通过双向链表存储
- 对于netty消息发送方式,写入到channel中的消息会从末尾开始流动,写入ChannelHandlerContext将会从ChannelPipeline中下一个ChannelHandler开始流动
.1.1.7. Netty处理器
- netty处理器包含入栈处理器和出栈处理器
- 入栈处理器顶层ChannelInboundHandler 出栈处理器顶层ChannelOutboundHandler
- 编码:message -> byte(ChannelOutboundHandler) 解码:byte -> message(ChannelInboundHandler)
- ReplayingDecoder extends ByteToMessageDecoder 会不断重试判断读取是否满足,因此不需要数据读取大小判断
.1.1.8. Tcp的黏包,拆包
- 黏包:tcp 在传输消息时,可能数据不够发送条件,需要合并消息
- 拆包:tcp 在数据接受时候,可能会收到多个消息合并后的数据包
- netty 提供前一条消息与下一条消息分割的分割符号识别
.1.1.9. Future
- JDK提供的Future只能通过手工方式检查结果,而这个操作是会阻塞的;Netty则对ChannelFuture进行了增强,通过ChannelFutrueListener以回调方式来获取执行结果,去除了手工检查阻塞的操作,值得注意的是:ChannelFutureListener的OperationComplete方法是由I/O线程执行的,因此要注意的是不要在这里执行耗时操作,否则需要通过另外的线程或线程池来执行