文章目录
一、IO的基本概念
1.阻塞与非阻塞
阻塞与非阻塞是描述进程在访问某个资源时,数据是否准备就绪的的一种处理方式。当数据没有准备就绪时:
阻塞:线程持续等待资源中数据准备完成,直到返回响应结果。
非阻塞:线程直接返回结果,不会持续等待 资源准备数据结束 后才响应结果。
2.同步与异步
同步与异步是指访问数据的机制。
同步:一般指主动请求并等待IO操作完成的方式。
异步:指主动请求数据后便可以继续处理其它任务,随后等待IO操作完毕的通知。
3.IO模型
五大IO模型
- 阻塞IO模型(传统IO模型)
- 非阻塞IO模型
- 多路复用IO模型
- 信号驱动IO模型
- 异步IO模型
参考地址:https://blog.csdn.net/ocean_fan/article/details/79622956
BIO
BIO ,全称 Block-IO ,是一种阻塞 + 同步的通信模式。
是一个比较传统的通信方式,模式简单,使用方便。但并发处理能力低,通信耗时,依赖网速。
NIO
NIO ,全称 New IO ,也叫 Non-Block IO
是一种非阻塞 + 同步的通信模式。
AIO
AIO ,全称 Asynchronous IO ,也叫 NIO2 ,是一种非阻塞 + 异步的通信模式。在 NIO 的基础上,引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现。
IO比较
比较项 | BIO | NIO | AIO |
---|---|---|---|
是否阻塞 | 阻塞 | 非阻塞 | 非阻塞 |
同步异步 | 同步 | 同步 | 异步 |
性能 | 低 | 一般 | 高 |
吞吐量 | 低 | 高 | 高 |
但是在绝大多数我们日常业务场景,NIO 和 AIO 的性能差距实际没这么大。在 Netty5 中,基于 AIO 改造和支持,最后发现,性能并没有想象中这么强悍,所以 Netty5 被废弃,而是继续保持 Netty4 为主版本,使用 NIO 为主。
二、NIO三大组件
1.Channel
Channel是一个双向通道,与传统IO操作只允许单向的读写不同的是,NIO的Channel允许在一个通道上进行读和写的操作
2.Buffer
它是一个缓冲区,实际上是一个容器,一个连续数组。Channel提供从文件、网络读取数据的渠道,但是读写的数据都必须经过Buffer。
3.Selector(多路复用器)
Selector与Channel是相互配合使用的,将Channel注册在Selector上之后,才可以正确的使用Selector,但此时Channel必须为非阻塞模式。Selector可以监听Channel的四种状态(Connect、Accept、Read、Write),当监听到某一Channel的某个状态时,才允许对Channel进行相应的操作。
几个状态:
■ Connect:某个Channel成功连接到另一个服务器称为“连接就绪”;
■ Accept:一个ServerSocketChannel准备好接收新进入的连接,称为“接收就绪”;
■ Read:一个有数据可读的通道可以说是“读就绪”;
■ Write:等待写数据的通道可以说是“写就绪”。
4. NIO流程
三、netty
netty简介
Netty是一个Java的开源框架。提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
Netty是一个NIO客户端,服务端框架。允许快速简单的开发网络应用程序。例如:服务端和客户端之间的协议,它简化了网络编程规范。
NIO和netty
NIO开发的问题
1、跨平台和兼容性问题:NIO依赖于操作系统,在Linux和Windows平台上表现的结果有所不同。
2、扩展ByteBuffer:ByteBuffer允许包装一个byte[]来获得一个实例,可以尽量减少内存拷贝。但是它不能被扩展,ByteBuffer的构造函数是私有的。
3、epoll BUG:可能会导致无效的状态和100%CPU利用率
Netty的优点:
1、API使用简单,开发门槛低;
2、功能强大,预置了多种编解码功能,支持多种主流协议;
3、定制功能强,可以通过ChannelHandler对通信框架进行灵活的扩展;
4、性能高,通过与其他业界主流的NIO框架对比,Netty综合性能最优;
5、成熟、稳定,Netty修复了已经发现的NIO所有BUG;
6、社区活跃;
7、经历了很多商用项目的考验。
Netty粘包和拆包
netty使用的是tcp协议,非http。
TCP是一个“流”协议,所谓流,就是没有界限的一串数据。可以想象为河流中的水,并没有分界线。TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包的划分,所以在业务上认为,一个完整的包可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包问题。
解决方案:
➢ 消息定长,例如每个报文的大小为固定长度200字节,如果不够,空位补空格;
➢ 在包尾增加回车换行符进行分割,例如FTP协议;
➢ 将消息分为消息头和消息体,消息头中包含消息总长度(或消息体总长度)的字段;
➢ 更复杂的应用层协议;
常见的几个编解码器:
1、 LineBasedFrameDecoder:以换行符为结束标志的编解码,支持携带结束符或者不携带结束符两种解码方式,同时支持配置单行的最大长度。
2、 DelimiterBasedFrameDecoder:实现自定义分隔符作为消息的结束标志,完成解码。
3、 FixedLengthFrameDecoder:是固定长度解码器
Netty性能优化序列化Protobuf
protobuf介绍
protobuf介绍:https://blog.csdn.net/jiaweiok123/article/details/87809831
protobuf使用
1.下载
protobuf下载地址:https://github.com/protocolbuffers/protobuf/releases/tag/v3.4.0
2.配置环境变量
输入protoc --version查看是否安装成功
3.编写proto文件
syntax = "proto3"; // PB协议版本
package com.zhaolaobao.netty.protobuf.pojo;// 包名 (生成java类第一行的package)
option java_package = "com.zhaolaobao.netty.protobuf.pojo"; // 实际生成的java类的路径,必须和package保持一致,否则java类报错
option java_outer_classname="Test"; // 生成类的类名,注意:下划线的命名会在编译的时候被自动改为驼峰命名
//生成的内部类的类名,注意不能与java_outer_classname相同
message Test2 {
int32 id = 1; // int 类型
string name = 2; // string 类型
string email = 3;
}
4.编写脚本并执行
protoc -I=./ --java_out=D:/workspace/zhaolaobao-saas/zhaolaobao-netty/src/main/java Test.proto
-l后面跟着proto文件的路径
–java-out跟着生成的java文件的路径(和proto文件的package组合成文件路径地址)
netty高性能
1.同步非阻塞通信模型
2.高效的Reactor线程模型
常用的Reactor线程模型有三种,分别如下:
- Reactor单线程模型;
- Reactor多线程模型;
- 主从Reactor多线程模型;
3.无锁化的串行设计
在大多数场景下,并行多线程处理可以提升系统的并发性能。但是,如果对于共享资源的并发访问处理不当,会带来严重的锁竞争,这最终会导致性能的下降。为了尽可能地避免锁竞争带来的性能损耗,可以通过串行化设计,既消息的处理尽可能在同一个线程内完成,期间不进行线程切换,这样就避免了多线程竞争和同步锁。
为了尽可能提升性能,Netty采用了串行无锁化设计,在IO线程内部进行串行操作,避免多线程竞争导致的性能下降。表面上看,串行化设计似乎CPU利用率不高,并发程度不够。但是,通过调整NIO线程池的线程参数,可以同时启动多个串行化的线程并行运行,这种局部无锁化的串行线程设计相比一个队列——多个工作线程模型性能更优。
4.高效的并发编程
Netty中高效并发编程主要体现:
- volatile的大量、正确使用;使用volatile来代替 synchronized关键字,提升并发访问的性能。
NioEventLoop.ioRatio - CAS和原子类的广泛使用;
ChannelOutboundBuffer.TOTAL_PENDING_SIZE_UPDATER AtomicLongFieldUpdater类型 - 线程安全容器的使用;
NioEventLoop.newTaskQueue() - 通过读写锁提升并发性能。
5.高性能的序列化框架
影响序列化性能的关键因素总结如下:
- 序列化后的码流大小(网络宽带的占用);
- 序列化与反序列化的性能(CPU资源占用);
- 是否支持跨语言(异构系统的对接和开发语言切换)。
Netty默认提供了对GoogleProtobuf的支持,通过扩展Netty的编解码接口,用户可以实现其他的高性能序列化框架。
其实就是因为Java序列化的性能太差,催生了很多各种高性能的开源序列化技术和框架(性能差只是其中一个原因,还有跨语言等因素)
6.零拷贝
零拷贝,只是减少了拷贝的次数。Netty的“零拷贝”主要体现在三个方面:
- Netty的接收和发送ByteBuffer采用DIRECT BUFFERS,使用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝。
- 第二种“零拷贝”的实现CompositeByteBuf,它对外将多个ByteBuf封装成一个ByteBuf,对外提供统一封装后的ByteBuf接口。
- 第三种“零拷贝”就是文件传输,Netty文件传输类DefaultFileRegion通过transferTo方法将文件发送到目标Channel中。
7.内存池
- 堆外内存回收
- 不用频繁分配内存
8.灵活的tcp参数配置
- SO_RCVBUF和SO_SNDBUF:通常建议值为128KB或者256KB;
- TCP_NODELAY:NAGLE算法通过将缓冲区内的小封包自动相连,组成较大的封包,阻止大量小封包的发送阻塞网络,从而提高网络应用效率。但是对于时延敏感的应用场景需要关闭该优化算法;