Netty知识点总结
2021-11-01 14:57:41 1 举报
AI智能生成
Netty知识点总结
作者其他创作
大纲/内容
1. NIO基础
1.IO 模型
非阻塞 vs 阻塞
强调进程/线程要访问的数据是否就绪,进程/线程是否需要等待,强调的数据准备这个过程;
同步/异步
同步和异步:同步和异步一般是面向操作系统与应用程序对IO操作的层面上来区别的。
访问数据的方式,同步需要主动读写数据,在读写数据的过程中还是会阻塞;异步只需要I/O操作完成的通知,并不主动读写数据,由操作系统内核完成数据的读写,强调I/O真正读写这个阶段。
阻塞 IO
<br>
非阻塞 IO
<br>
多路复用
<br>
异步 IO
<br>
阻塞 IO vs 多路复用
<br>
2. NIO vs BIO vs AIO
1.BIO(同步阻塞IO)
数据的读取写入必须阻塞在一个线程内等待其完成<br>线程数量:客户端并发访问数为1:1,由于线程是Java虚拟机中非常宝贵的资源,一旦线程数急剧增加,系统性能会急剧下降,导致线程栈溢出,创建新的线程失败,并最终导致宕机<br>
2.NIO(同步非阻塞IO)
用NIO方式处理IO使用多路复用器Selector来轮询每个通道Channel,当通道中有事件时就通知处理,不过使用起来相当复杂。
三大组件
Channel
channel它就是读写数据的**双向通道**,可以从 channel 将数据读入 buffer,也可以将 buffer 的数据写入
Buffer(缓冲区)
与Channel进行交互,数据是从Channel读入缓冲区,从缓冲区写入Channel中的
Selector(选择器、多路复用器)
<br>
3.AIO(真正的异步非阻塞IO)
4.零拷贝
传统 IO 问题
图片
<br>
流程
NIO 优化
图片
<br>
流程
<br>
2. Netty 入门
1.Netty 是什么
Netty是基于Java NIO client-server的网络应用框架,使用Netty可以快速开发网络应用。<br>Netty的内部实现是很复杂的,但是Netty提供了简单易用的API从网络处理代码中解耦业务逻辑。<br>Netty是完全基于NIO实现的,所以整个Netty都是异步的。
特点
高并发、传输快、封装好<br>
优势
使用简单、功能强大、定制能力强、性能高、稳定、社区活跃<br>
高性能原因<br>
IO线程模型(IO多路复用)<br>
同步非阻塞、用更少的资源干更多的事
内存零拷贝<br>
不需要将buffer从一个内存区域移动到另一个内存区域
从OS角度:避免在用户态和内核态之间拷贝数据
netty的Zero-copy 与上面我们所提到到 OS 层面上的 Zero-copy 不太一样, <br>Netty的 Zero-coyp 完全是在用户态(Java 层面)的, <br>它的 Zero-copy 的更多的是偏向于 优化数据操作 这样的概念<br>
Netty 提供了 CompositeByteBuf 类, 它可以<font color="#f15a23">将多个 ByteBuf 合并为一个逻辑上的 ByteBuf,</font> 避免了各个 ByteBuf 之间的拷贝.
通过 wrap 操作, 我们可以<font color="#f15a23">将 byte[] 数组、ByteBuf、ByteBuffer等包装成一个 Netty ByteBuf 对象</font>, 进而避免了拷贝操作.
ByteBuf 支持 <font color="#f15a23">slice 操作, 因此可以将 ByteBuf 分解为多个共享同一个存储区域的 ByteBuf,</font> 避免了内存的拷贝.
通过 FileRegion 包装的FileChannel.<font color="#f15a23">tranferTo 实现文件传输, 可以直接将文件缓冲区的数据发送到目标 Channel,</font> 避免了传统通过循环 write 方式导致的内存拷贝问题.
内存池设计<br>
申请的内存可以重复使用(直接内存)
内部通过一个二叉查找树管理内存分配情况<br>
串行化处理读写<br>
避免使用锁带来的性能开销<br>
高性能序列化协议<br>
支持protobuf等高性能序列化协议<br>
<font color="#f15a23"><b>默认</b></font>情况下Netty<font color="#f15a23"><b>启动CPU 处理器两倍的线程</b></font>,bind 完之后启动。
支持的心跳类型<br>
eaderIdleTime:为读超时时间(即测试端一定时间内未接受到被测试端消息)。
writerIdleTime:为写超时时间(即测试端一定时间内向被测试端发送消息)。
allIdleTime:所有类型的超时时间。
2. 开发一个简单的服务器端和客户端
流程梳理
<br>
服务器端
代码
客户端
代码<br>
Netty执行大致流程
3.reactor线程模型<br>
单线程模型
所有操作都在同一个NIO线程处理,在这个单线程中要负责接收请求,处理IO,编解码所有操作,相当于一个饭馆只有一个人,同时负责前台和后台服务,效率低。
图片<br>
<br>
多线程模型
有一个NIO 线程(Acceptor)接收客户端的TCP 连接请求,一组线程池负责网络IO 的操作,即消息的读取、解码、编码和发送;相当于一个饭馆有一个前台负责接待,有很多服务员去做后面的工作,这样效率就比单线程模型提高很多。
图片
<br>
主从多线程模型
<font color="#f15a23">一组线程</font>(Acceptor) 负责接收客户端的TCP 连接请求;<br><font color="#f15a23">一组线程</font>负责网络IO 的操作,即消息的读取、解码、编码和发送;<br>
图片
<br>
Netty采用了第三种模型:主从线程模型
netty中的具体运用:<br>基于多路复用器接收并处理用户请求,内部实现了<b><u>两个线程池,boss线程池和work线程池</u></b>,<br><b>boss线程池的线程负责处理请求的accept事件</b>,当接收到accept事件的请求时,把对应的socket封装到一个Channel中,并交给work线程池,<br><b>work线程池负责请求的read和write事件</b>,由对应的Handler处理<br>
4. 组件
EventLoop(事件循环对象)
EventLoop 本质是一个单线程执行器(同时维护了一个 Selector),里面有 run 方法处理 Channel 上源源不断的 io 事件。
EventLoopGroup(事件循环组)
包含一个或多个EventLoop<br>
EventLoopGroup 是一组 EventLoop,Channel 一般会调用 EventLoopGroup 的 register 方法来绑定其中一个 EventLoop,后续这个 Channel 上的 io 事件都由此 EventLoop 来处理(保证了 io 事件处理时的线程安全)
Channel
socket
一个channel在生命周期内只注册一于个EventLoop
ChannelFuture
异步通知
Netty 框架中所有的 I/O 操作都为异步的,因此我们需要 ChannelFuture 的 addListener()注册一个 <b>ChannelFutureListener 监听事件</b>,当操作执行成功或者失败时,监听就会<b>自动触发返回结果</b>
ChannelHandler
所有处理入站和处理出站数据的应用程序逻辑的地方<br>
主要用来处理各种事件,这里的事件很广泛,比如可以是连接、数据接收、异常、数据转换等
ChannelPipeLine<br>
提供了ChannelHandler 链的容器,并定义了用于在该链上传播入站和出站事件流的API
ChannelHandlerContext
当ChannelHandler 被添加到ChannelPipeline 时,它将会被分配一个ChannelHandlerContext,其代表了ChannelHandler 和ChannelPipeline 之间的绑定
ByteBuf
Netty 的ByteBuffer 替代品是ByteBuf,一个强大的实现,既解决了JDK API 的局限性,又为网络应用程序的开发者提供了更好的API。<br>
ByteBuf 维护了<b>两个不同的索引</b>,名称以read 或者write 开头的ByteBuf 方法,将会推进其对应的索引,而名称以set 或者get 开头的操作则不会<br>
使用<br>
堆缓冲区
直接缓冲区<br>
复合缓冲区
概念及API<br>
随机访问/顺序访问/读写操作<br>
可丢弃字节/可读字节/可写字节<br>
索引管理<br>
查找操作<br>
派生缓冲区<br>
引用计数<br>
<br>
零拷贝技术
<font color="#f15a23">支持自动扩容(4M)</font>,保证put方法不会抛出异常、通过内置的复合缓冲类型,实现零拷贝(zero-copy);<br>不需要调用flip()来切换读/写模式,读取和写入索引分开;方法链;<br>引用计数基于AtomicIntegerFieldUpdater用于内存回收;<br>PooledByteBuf采用二叉树来<font color="#f15a23">实现一个内存池,集中管理内存的分配和释放</font>,不用每次使用都新建一个缓冲区对象。<br>UnpooledHeapByteBuf每次都会新建一个缓冲区对象。<br>
3. Netty 进阶
1.编解码技术
Java序列化
Java序列化的目的主要有两个:1.网络传输。2.对象持久化。
Java序列化的缺点:1.无法跨语言。 2.序列化后码流太大。3.序列化性能太低。
其他序列化框架
Google的Protobuf
Protobuf全称Google Protocol Buffers,它由谷歌开源而来,在谷歌内部久经考验。它将数据结构以.proto文件进行描述,通过代码生成工具可以生成对应数据结构的POJO对象和Protobuf相关的方法和属性
Protostuff
Protostuff是基于大名鼎鼎的Google protobuff技术的Java版,它较于protobuf最明显的好处是,在几乎不损耗性能的情况下做到了不用我们写.proto文件来实现序列化。Protostuff反序列化时并不要求类型匹配,比如包名、类名甚至是字段名,它仅仅需要序列化类型A 和反序列化类型B 的字段类型可转换(比如int可以转换为long)即可<br>
Apache的Thrift
Thrift源于Facebook,Thrift可以支持多种程序语言,如C++、C#、Cocoa、Erlang、Haskell、Java、Ocami、Perl、PHP、Python、Ruby和Smalltalk。在多种不同的语言之间通信,Thrift可以作为高性能的通信中间件使用,它支持数据(对象)序列化和多种类型的RPC服务。Thrift适用于静态的数据交换,需要先确定好它的数据结构,当数据结构发生变化时,必须重新编辑IDL文件,生成代码和编译,这一点跟其他IDL工具相比可以视为是Thrift的弱项。Thrift适用于搭建大型数据交换及存储的通用工具,对于大型系统中的内部数据传输,相对于JSON和XML在性能和传输大小上都有明显的优势。<br>
JBoss Marshalling
JBoss Marshalling是一个Java对象的序列化API包,修正了JDK自带的序列化包的很多问题,但又保持跟java.io.Serializable接口的兼容;同时增加了一些可调的参数和附加的特性,并且这些参数和特性可通过工厂类进行配置。
Avro
Hadoop的一个子项目,解决了JSON的冗长和没有IDL的问题
优点
支持丰富的数据类型、简单的动态语言结合功能、具有自我描述属性、提高了数据解析速度、快速可压缩的二进制数据形式、可以实现远程过程调用RPC、支持跨编程语言实现
缺点
<b>对于习惯于静态类型语言的用户不直观</b>
适用场景
在Hadoop中做Hive、Pig和MapReduce的持久化数据格式
Message pack
一个高效的二进制序列化格式
Hessian <br>
采用二进制协议的轻量级remoting onhttp工具
kryo
基于protobuf协议,只支持java语言,需要注册(Registration),然后序列化(Output),反序列化(Input)
编码器
负责将消息从字节或其他序列形式转成指定的消息对象
解码器
将字节解码为消息<br>
将一种消息类型解码为另一种<br>
TooLongFrameException<br>
2.Netty之TCP粘包/拆包
解决方案
短连接
固定长度
固定分隔符
预设长度
原因
应用程序写入数据的字节大小大于<u>套接字</u>发送缓冲区的大小<br>进行MSS大小的TCP分段<br>以太网的payload大于MTU进行IP分片<br>
解决方式
1.消息定长,例如每个报文的大小为固定长度200字节,如果不够,空位补空格。
FixedLengthFrameDecoder类
2.报文 尾增加特殊字符分割,例如FTP协议。
行分隔符类:LineBasedFrameDecoder
自定义分隔符类 :DelimiterBasedFrameDecoder
3.将消息分为消息头和消息体,消息头中包含表示消息总长度(或者消息体长度)的字段,通常设计思路为消息头的第一个字段使用int32来表示消息的总长度。
LengthFieldBasedFrameDecoder类。分为有头部的拆包与粘包、长度字段在前且有头部的拆包与粘包、多扩展头部的拆包与粘包。
3. 协议设计与解析
为什么需要协议
自定义协议
编解码器
@Sharable
自定义协议要素
4.实现一个轻量级分布式RPC框架
设计思路
服务发布与订阅:服务端使用Zookeeper注册服务地址,客户端从Zookeeper获取可用的服务地址。<br><br>通信:使用Netty作为通信框架。<br><br>Spring:使用Spring配置服务,加载Bean,扫描注解。<br><br>动态代理:客户端使用代理模式透明化服务调用。<br><br>消息编解码:使用Protostuff序列化和反序列化消息。
5.TCP连接之负载均衡和双向通信<br>
TCP连接负载均衡
nginx或haproxy做集群TCP负载均衡,用户session信息存到redis
服务端向客户端发送TCP消息
服务端向客户端上传消息时保存ChannelHandlerContext到内存,下发消息时所有的服务器都推送一遍
4. Netty 源码
1. 启动流程剖析
2.EventLoop 剖析
3. accept 流程剖析
4. read 流程剖析
0 条评论
下一页