继续netty相关的学习

零拷贝指没有CPU拷贝

  • 传统IO拷贝(hard drive-->kernel buffer-->user buffer-->socket buffer-->protocol engine)DMA2次,CPU2次。
  • mmap(kernel buffer和user buffer共享数据,少一次拷贝)
  • sendFile(不经过用户态,kernel buffer-->socket buffer) linuX2.1
  • sendFile(linux2.4)kernel buffer-(DMA)->protocol engine(少量拷贝到socket buffer可忽略)
  • NIO的channel.transferTo方法底层使用零拷贝

mmap和sendFile的区别
1)mmap 适合小数据量读写,sendFile 适合大文件传输。
2)mmap 需要4次上下文切换,3次数据拷贝;sendFile需要3次上下文切换,最少2次数据拷贝。
3)sendFile可以利用DMA方式,减少CPU拷贝,mmap则不能(必须从内核拷贝到Socket缓冲区)。

【Netty】

  • 为什么要⽤ Netty?
  • Netty线程模型(主从Reactor多线程改进)IO复用结合线程池,分发者模式
  • Netty核心组件
  • EventloopGroup 了解么?和 EventLoop 啥关系?
  • Bootstrap 和 ServerBootstrap 了解么?
  • NioEventLoopGroup 默认的构造函数会起多少线程?(CPU核数*2)
  • Netty 服务端和客户端的启动过程了解么?
  • Netty 的零拷⻉了解么?
  • TCP 粘包/拆包?有什么解决办法呢?
  • Netty ⻓连接、⼼跳机制了解么?

1)Netty抽象出两组线程池BossGroup专门负责接收客户端的连接,WorkerGroup专门负责网络的读写
2)BossGroup和WorkerGroup类型都是NioEventLoopGroup
3)NioEventLoopGroup相当于一个事件循环组,这个组中含有多个事件循环,每一个事件循环是NioEventLoop
4)NioEventLoop表示一个不断循环的执行处理任务的线程,每个NioEventLoop都有一个selector,用于监听绑定在其上的socket的网络通讯
5)NioEventLoopGroup可以有多个线程,即可以含有多个NioEventLoop
6)每个Boss NioEventLoop循环执行的步骤有3步

  • 轮询accept事件
  • 处理accept事件,与client建立连接,生成NioScocketChannel,并将其注册到某个worker NIOEventLoop上的selector
  • 处理任务队列的任务,即runAllTasks
    7)每个Worker NIOEventLoop循环执行的步骤
  • 轮询read,write事件
  • 处理io事件,即read,write事件,在对应NioScocketChannel处理
  • 处理任务队列的任务,即runAllTasks
    8)每个Worker NIOEventLoop 处理业务时,会使用pipeline(管道),pipeline中包含了channel,即通过pipeline可以获取到对应通道,管道中维护了很多的处理器

netty核心组件:
1.Channel
Channel接口是Netty对网络操作抽象类,它除了包括基本的I/0操作,如bind()、connect()、read()、write()等。比较常用的Channel接口实现类是NioServerSocketChannel(服务端)和NioSocketChannel(客户端),这两个Channel可以和BI0编程模型中的ServerSocket以及Socket两个概念对应上。Netty的Channel接口所提供的API,大大地降低了直接使用Socket类的复杂性。
2.EventLoop(聚合了selector选择器)
EventLoop(事件循环)接口,EventLoop定义了Netty的核心抽象,用于处理连接的生命周期中所发生的事件。
EventLoop的主要作用实际就是负责监听网络事件并调用事件处理器进行相关I/0操作的处理。
那Channel和EventLoop直接有啥联系呢?
Channel为Netty 网络操作(读写等操作)抽象类,EventLoop负责处理注册到其上的Channel处理I/0操作,两者配合参与I/0操作。
3.ChannelFuture
Netty是异步非阻塞的,所有的I/0操作都为异步的。
因此,我们不能立刻得到操作是否执行成功,但是,你可以通过ChannelFuture接口的addListener()方法注册一个 ChannelFutureListener,当操作执行成功或者失败时,监听就会自动触发返回结果。
并且,你还可以通过ChannelFuture的channel()方法获取关联的Channel
另外,我们还可以通过ChannelFuture 接口的sync()方法让异步的操作变成同步的。
4.ChannelHandler 和ChannelPipeline
ChannelHandler 是消息的具体处理器。他负责处理读写操作、客户端连接等事情。
ChannelPipeline为ChannelHandler的链,提供了一个容器并定义了用于沿着链传播入站和出站事件流的API。当Channel被创建时,它会被自动地分配到它专属的 ChannelPipeline。
我们可以在ChannelPipeline 上通过addLast()方法添加一个或者多个ChannelHandler],因为一个数据或者事件可能会被多个Handler处理。当一个ChannelHandler 处理完之后就将数据交给下一个ChannelHandler。

EventLoopGroup :
EventLoopGroup 包含多个EventLoop(每一个EventLoop 通常内部包含一个线程),EventLoop的主要作用实际就是负责监听网络事件并调用事件处理器进行相关I/0操作的处理。并且EventLoop 处理的I0事件都将在它专有的Thread 上被处理,即Thread和EventLoop 属于1:1的关系,从而保证线程安全。
Boss Event1oopGroup 用于接收连接,Worker EventloopGroup用于具体的处理(消息的读写以及其他逻辑处理)。当客户端通过connect 方法连接服务端时,bossGroup 处理客户端连接请求。当客户端处理完成后,会将这个连接提交给 workerGroup来处理,然后workerGroup负责处理其I0相关操作。

Bootstrap和 ServerBootstrap :引导/启动辅助类
1.Bootstrap 通常使用connet()方法连接到远程的主机和端口,作为一个Netty TCP协议通信中的客户端。另外,Bootstrap 也可以通过bind()方法绑定本地的一个端口,作为UDP协议通信中的一端。
2.ServerBootstrap通常使用bind()方法绑定本地的端口上,然后等待客户端的连接。
3.Bootstrap 只需要配置一个线程组-EventLoopGroup,而ServerBootstrap 需要配置两个线程组-EventLoopGroup,一个用于接收连接,一个用于具体的处理。

Netty 的零拷⻉:
在0S层面上的Zero-copy 通常指避免在|用户态(User-space)与|内核态(Kernel-space)
之间来回拷贝数据。而在Netty层面,零拷贝主要体现在对于数据操作的优化。
Netty中的零拷贝体现在以下几个方面
1.使用Netty 提供的CompositeByteBuf 类,可以将多个ByteBuf 合并为一个逻辑上的ByteBuf,避免了各个ByteBuf 之间的拷贝。
2.ByteBuf 支持slice操作,因此可以将ByteBuf分解为多个共享同一个存储区域的ByteBuf,避免了内存的拷贝。
3.通过FileRegion 包装的FileChannel.tranferTo实现文件传输,可以直接将文件缓冲区的数据发送到目标 Channel,避免了传统通过循环write方式导致的内存拷贝问题.

TCP 粘包/拆包?有什么解决办法呢?
1.使⽤ Netty ⾃带的解码器
eg: LineBasedFrameDecoder:发送端发送数据包的时候,每个数据包之间以换行符作为分隔,LineBasedFrameDecoder的工作原理是它依次遍历ByteBuf 中的可读字节,判断是否有换行符,然后进行相应的截取。
2.自定义序列化编解码器
在Java中自带的有实现Serializable接口来实现序列化,但由于它性能、安全性等原因一般情况下是不会被使用到的。通常情况下,我们使用Protostuff、Hessian2、json 序列方式比较多

netty心跳机制:
在TCP保持长连接的过程中,可能会出现断网等网络异常出现,异常发生的时候,client与server之间如果没有交互的话,它们是无法发现对方已经掉线的。为了解决这个问题,我们就需要引入心跳机制。
心跳机制的工作原理是:在client与server 之间在一定时间内没有数据交互时,即处于idle状态时,客户端或服务器就会发送一个特殊的数据包给对方,当接收方收到这个数据报文后,也立即发送一个特殊的数据报文,回应发送方,此即一个PING-PONG交互。所以,当某一端收到心跳消息后,就知道了对方仍然在线,这就确保TCP连接的有效性.
TCP实际上自带的就有长连接选项,本身是也有心跳包机制,也就是TCP的选项:SO-KEEPALIVE。
但是,TCP协议层面的长连接灵活性不够。所以,一般情况下我们都是在应用层协议上实现自定义心跳机制的,也就是在Netty层面通过编码实现。通过Netty实现心跳机制的话,核心类是IdleStateHandler。