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