Netty常见问题

为什么选择Netty

  • 原生的NIO有JDK epoll空轮询bug

bug描述

对于突然中断的Socket连接,jdk底层的对epoll的实现可能出现bug,这个bug表现为selector.select()不会阻塞,而是直接返回,此时是并没有事件发生的。因此select()的返回值会为0,那么while循环就会一直循环执行,最终可能导致cpu占用率达到100%

Netty如何解决这个问题的?

既然是selector出现了问题,那么就当然是应当要重建selector了。netty会维护一个记录当前select()返回值为0的次数,如果次数大于netty的规定值,那么会调用rebuilder()对其进行重建

重建步骤:

1 新建seletor()

2 将原来selector上的channel注册到新的channel上

  • Nio的api比较复杂

  • 原生Nio开发需要对线程池特别熟悉,因为涉及到Reactor模型

 

Reactor线程模型

Reactor模式是基于IO多路复用的,IO多路复用主要表示为,用一条线程,监听多个感兴趣的事件。Netty的reactor模式中有2个reactor,1个用于监听新连接,另一个用于监听已建立的连接的活动。Netty为了充分利用多核cpu还采用了线程池

 

 

 

TCP 粘包/拆包 

https://www.cnblogs.com/wade-luffy/p/6165671.html

是什么

由于Tcp是面向字节流的,而服务端又存在着流量控制这样的策略,因此,对于服务端,一次性读到多少字节的数据是不确定的,因此会发生第一次发送的包与第二次发送的包粘在一起,或者第一次发送的包被分割为2份的情况。

怎么解决

1 对数据进行定长。如规定每次发生的数据都为200byte,不足的部分补空字符

2 将数据分为数据头与数据体的2部分,数据头中存放整个数据的长度(或数据体部分的长度)。http1.1为了采用管道这种方式,也就在http头增加了一个content-length

3 在数据结束部分添加结束符号

Netty 的零拷贝

在nio的学习中,我们知道,nio将最耗时的io操作(填充,读取缓冲区)转移到了操作系统,这部分内存(ByteBuffer)被称为堆外内存。

是在发送数据的时候,传统的实现方式是:

1. `File.read(bytes)`
2. `Socket.send(bytes)`
这种方式需要四次数据拷贝和四次上下文切换:

1. 数据从磁盘读取到内核的read buffer

2. 数据从内核缓冲区拷贝到用户缓冲区
3. 数据从用户缓冲区拷贝到内核的socket buffer
4. 数据从内核的socket buffer拷贝到网卡接口(硬件)的缓冲区 

明显第2,3部分是无用的,因为我们根本不需要将数据读到用户内存中。

零拷贝

1. 调用transferTo,数据从文件由DMA引擎拷贝到内核read buffer

2. 接着DMA从内核read buffer将数据拷贝到网卡接口buffer

上面的两次操作是居于DMA的操作,因此不需要CPU参与,所以就达到了零拷贝。

参考 https://juejin.im/post/5bdaf8ea6fb9a0227b02275a

发布了40 篇原创文章 · 获赞 6 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_41332728/article/details/101108903