目录
1. 学习目的
- BIO、NIO和AIO的区别?
- NIO的组成?
- Netty的特点?
- Netty的线程模型?
- TCP 粘包/拆包的原因及解决方法?
- 了解哪几种序列化协议?
- 如何选择序列化协议?
- Netty的零拷贝实现?
- Netty的高性能表现在哪些方面?
- NIOEventLoopGroup源码?
2.学习Netty
- 为什么学习Netty
分布式的时代 分布式框架中要用到
Spring5 去servlet化,底层使用netty
SpringBoot 内部实现web容器
Zookeeper 底层通信
Dubbo 分布式服务框架,多协议支持(RPC) Netty
为成为架构师筑基 - Netty能帮助我们解决什么问题?
框架:简化开发一系列解决方案的集合
封装IO操作的框架
复杂的业务场景中,没有说用一个单独的IO APi,经常遇到的问题:手写线程(Thread),多线程处理、性能问题、单双工,影响业务的开发。
IO + 多线程来解决问题
类似的框架:Mina Netty的前身(不是一个人开发的) - 为什么要封装IO操作
阻塞和非阻塞:
自己回答:处理端在处理数据的时候,如果被处理对象的数据没有准备好,线程会阻塞在当前线程,譬如socket.accept()方法 ,在等待网络连接的时候,如果没有请求连接,线程会处于阻塞状态,如果是非阻塞模型,当前线程可以去处理其他事物
Input Output
是相对于内存而言
磁盘只是input output的一端,除了磁盘还有网络
IO模型
阻塞非阻塞:参照IO操作 读数据或取数据时候的一种处理机制
BIO阻塞:
流
NIO:
通道 Buffer 轮询
同步与异步
在处理数据的时候,在同一时间点能同时做多个处理:异步
在同一时间只能做一个处理:同步
- 多路复用体现:
ServerSocketChannel
SocketChannle (从多路复用器中拿到客户端的引用)
都来自于 (SelectionKey)key.channel
NIO的操作过于繁琐,于是有了Netty,对NIO进行了封装。
IO(BIO)Block IO 同步阻塞IO
NIO Non-Block IO 同步非阻塞IO (可以使用线程池的方式,实现异步)
AIO Async IO 异步非阻塞IO (事件驱动,回调实现异步)
3. NIO
3.1 NIO的核心组件(三件套)
- Buffer、Selector、Channel、
3.1.1 Buffer
- 缓冲区属性
容量:capacity:表示该缓冲区可以保存多少数据。
极限:limit:表示缓冲区的当前终点,不能对缓冲区中超过极限的数据进行读写操作。极限可以修改,有利于缓冲区的重用。
位置:position:表示缓冲区中下一个读写单元的位置,每次读写缓冲区的数据时,都会改变位置值,为下一次读写数据做准备。
属性关系为 capacity>=limit>=position>=0 - 改变缓冲区3个属性的方法
clear():把极限限制为容量值,再把位置设为0.
flip():把极限值设为位置值,再把位置设为0.
rewind():不改变极限,把位置设为0. - ByteBuffer类没有提供公开的构造方法,但是提供了两个获得ByteBuffer实例的静态工厂方法。
allocate(int capacity):返回一个ByteBuffer对象
directAllocate(int capacity):返回一个ByteBuffer对象,称为直接缓冲区 - 所有的缓冲区都提供了读写缓冲区的方法
get():相对读。从缓冲区的当前位置读取一个单元的数据,读完后把位置值加1。
get(index):决对读。从参数index指定的位置读取一个单元的数据。
put():相对写。向缓冲区的当前位置写入一个单元的数据,写完后把位置值加1.
put(int index):绝对写。向参数index指定的位置写入一个单元的数据。
- buffer操作的流程
3.1.2 Selector
3.1.3 Channel
是多路复用的基础
解决的问题:
- 请求过多,导致连接线程过多
- 处理数据较大,导致线程长时间占用 也会导致阻塞
selector模型,IO调用不会被阻塞,
3.2何为多路复用
3.3Netty支持的功能与特性
Netty
Netty封装了NIO,Reactor模型,声明BOSSS线程,worker线程,可以修改BOSS线程和worker线程的数量
- 编码过程
//Netty服务 //NIO //BIO
ServerBootstrap ServerSocketChannel ServerSocket
主线程处理类,这样的写法,底层使用反射
子线程处理类 ,Handler
无锁化串行编程
编码器
解码器
业务处理逻辑
针对主线程的最大配置数
针对子线程的配置,保持长连接
//SocketChannel的封装
ChannelHandlerContext
- 总结
Netty就是一个同时支持多协议的网络通信框架
官网:
Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.
Netty是一个异步事件驱动的网络应用框架,用于快速开发可维护的高性能协议服务器和客户端。
协议编解码
2. Netty支持的协议
官网图
Netty的高性能
比传统IO性能提升了8倍多。
- 传统RPC调用性能差的三宗罪
- 阻塞IO不具备弹性伸缩能力(一个请求,一个应答),高并发导致宕机
- Java序列化编码、解码的性能问题,直接是字节流的读写
- 传统IO线程模型过多占用CPU资源
- 高性能的三个主题:
IO模型 传输
数据协议 协议 (协议越简单,通信效率越高)
线程模型 线程 (编解码的处理) - 异步非阻塞通信
NioEventLoop聚合了多路复用器Selector,可以同时并发处理成百上千个客户端Channel,由于读写操作都是非阻塞的,可以充分提升IO线程的运行效率,避免由于频繁阻塞IO阻塞导致的线程挂起。 - 零拷贝(直接缓冲区)
- 接收和发送ByteBuffer使用堆外内存直接内存进行socket读写。
- 提供了组合Buffer对象,可以聚合多个ByteBuffer对象。
- transfreTo()直接将文件缓冲区的数据发送到目标Channel,避免循环导致内存拷贝的问题。
- 内存池
三个维度
- Pooled与unPooled(池化与非池化)
- UnSage和非UnSafe(底层读写与应用程序读写)
- Heap与Direct(堆内存与堆外内存)
netty有pooled buffer 和 unpooled buffer
pooled 先初始化一定大小的空间 ,读写效率 ,内存分配 堆外内存读写高,但是内存管理比较麻烦。
- 高效的Reactor线程模型
- Reactor单线程模型
netty的接收连接,分发调度、读写都由一个线程完成。 - Reactor多线程模型
反应接待线程、反应线程池(处理读写,编解码) - 主从Reactor多线程模型
接收线程 主线程池(调度不同模块业务的资源) 派发不同的业务逻辑线程
从线程
- 无锁化的串行设计理念
Pipeline(管道) pipeline的添加是有顺序的。
pipeline的工作模式:责任链模式,双向链表 两种类型的handle
Inbound OutBound 负责数据处理的触发和回调 什么时候触发,什么时候回调
pipeline可以巧妙的解决权限问题,有一个好用的东西,事件传播,异常和消息可以一层层往上传。
MessageToMessageEncoder extends ChannelOutboundHandlerAdapter 是一个OutBound,OutBound说明数据需要一个回调处理了。
- 高效的并发编程 Netty的高效并发编程主要体现在如下几点:
- volatile的大量、正确使用
- CAS和原子类的广泛使用
- 线程安全容器的使用
- 通过读写锁提升并发性能
- 高性能的序列化框架
影响序列化性能的关键因素总结如下:
- 序列化后的码流大小(影响网络带宽的占用)
- 序列化&反序列化的性能(CPU资源占用)
- 是否支持跨语言(异构系统的对接和开发语言切换)
- 灵活的TCP参数配置能力
Netty核心之服务端Channel源码分析
服务端Channel的创建
bind()[用户代码入口]
initAndRegister()[初始化并注册]
newChannel()[创建服务端channel]
init()[初始化服务端Channel]