BIO、NIO
BIO :同步阻塞,数据的读取写入必须阻塞在一个线程内等待其完成
NIO : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理
区别:
1.BIO是面向流的,NIO是面向缓冲区的
2.BIO的各种流是阻塞的。而NIO是非阻塞的
3.BIO的Stream是单向的,而NIO的channel是双向的
Netty 是什么
一个异步事件驱动的网络应用框架,用于快速开发可维护的高性能协议服务器和客户端。Netty是基于nio的,封装了jdk的nio,使用起来更加方法灵活
具有以下特点:
高并发:基于 NIO开发的网络通信框架,对比于 BIO它的并发性能得到了很大提高。
传输快:传输依赖于零拷贝特性,尽量减少不必要的内存拷贝,实现了更高效率的传输。
封装好:封装了 NIO 操作的很多细节,提供了易于使用调用接口。
重要组件
Channel:网络通信的组件,能够用于执行网络I/O操作
EventLoop:主要是配合 Channel 处理 I/O 操作,用来处理连接的生命周期中所发生的事情。
ChannelFuture:Netty中所有的 I/O 操作都为异步的,因此我们需要 ChannelFuture 的 addListener()注册一个 ChannelFutureListener 监听事件,当操作执行成功或者失败时,监听就会自动触发返回结果。
ChannelHandler:所有处理入站和出站数据的逻辑容器。ChannelHandler 主要用来处理各种事件,这里的事件很广泛,比如可以是连接、数据接收、异常、数据转换等。
ChannelHandlerContext:Channel相关的所有上下文信息,同时关联一个ChannelHandler对象
ChannelPipeline:为 ChannelHandler 链提供了容器,当 channel 创建时,就会被自动分配到它专属的 ChannelPipeline,这个关联是永久性的。
零拷贝
Netty 的零拷贝主要包含三个方面:
1.接收和发送 ByteBuffer 采用直接内存,使用堆外直接内存进行 Socket 读写,不需要进行字节缓冲区的二次拷贝。如果使用传统的堆内存进行 Socket 读写,JVM 会将堆内存 Buffer 拷贝一份到直接内存中,然后才写入 Socket 中。相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。
2.提供了组合 Buffer 对象,可以聚合多个 ByteBuffer 对象,用户操作更加方便,避免了传统通过内存拷贝的方式将几个小 Buffer 合并成一个大的 Buffer。
3.文件传输采用了 transferTo 方法,它可以直接将文件缓冲区的数据发送到目标 Channel,避免了传统通过循环 write 方式导致的内存拷贝问题。
线程模型
通过反应器模型(Reactor)基于多路复用器接收并处理用户请求,内部实现了两个线程池,主线程池和工作线程池,其中主线程池的线程负责处理客户端连接,当接收到连接时,把对应的socket封装到一个NioSocketChannel中,并交给工作线程池,其中工作线程池负责请求的read和write事件,由对应的Handler处理。
单线程模型:所有I/O操作都由一个线程完成,即多路复用、事件分发和处理都是在一个Reactor线程上完成的。既要接收客户端的连接请求,向服务端发起连接,又要发送/读取请求或应答/响应消息。一个NIO 线程同时处理成百上千的链路,性能上无法支撑,速度慢,若线程进入死循环,整个程序不可用,对于高负载、大并发的应用场景不合适。
多线程模型:有一个NIO 线程(Acceptor) 只负责监听服务端,接收客户端的TCP 连接请求;NIO 线程池负责网络IO 的操作,即消息的读取、解码、编码和发送;1 个NIO 线程可以同时处理N 条链路,但是1 个链路只对应1 个NIO 线程,这是为了防止发生并发操作问题。但在并发百万客户端连接或需要安全认证时,一个Acceptor 线程可能会存在性能不足问题。
主从多线程模型:主线程用于绑定监听端口,接收客户端连接,将SocketChannel 从主线程池的Reactor 线程的多路复用器上移除,重新注册到从线程池的线程上,用于处理I/O 的读写等操作,从而保证mainReactor只负责接入认证、握手等操作;
粘包/拆包
TCP粘包/分包的原因:
应用程序写入的字节大小大于套接字发送缓冲区的大小,会发生拆包现象,而应用程序写入数据小于套接字缓冲区大小,网卡将应用多次写入的数据发送到网络上,这将会发生粘包现象;
解决方法:
使用定长的解码器、使用分隔符解码器或第三方序列化协议,如:protobuf
序列化协议
Java默认提供的序列化:无法跨语言、序列化后的码流太大、序列化的性能差
JSON:轻量级的数据交换格式,兼容性高、数据格式比较简单,易于读写、序列化后数据较小
Protobuf:序列化后码流小,性能高、结构化数据存储格式(XML JSON等)、通过标识字段的顺序,可以实现协议的前向兼容、结构化的文档更容易管理和维护
链路有效性检测
心跳检测的目的是确认当前链路可用,对方是否活着并且能够正常接收和发送消息。Netty提供TCP的Keep-Alive机制和基于链路空闲的心跳检测机制:
1.读空闲:链路持续时间t没有读取到任何消息
2.写空闲:链路持续时间t没有发送任何消息
3.读写空闲:链路持续时间t没有接收或者发送任何消息