Netty知识汇总

42次阅读

共计 7670 个字符,预计需要花费 20 分钟才能阅读完成。

1、TCP、UDP 的区别?

TCP 与 UDP 区别总结:

1)、TCP 面向连接(如打电话要先拨号建立连接);UDP 是无连接的,即发送数据之前不需要建立连接。

2)、TCP 提供可靠的服务。也就是说,通过 TCP 连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP 尽最大努力交付,即不保证可靠交付

3)、TCP 面向字节流,实际上是 TCP 把数据看成一连串无结构的字节流;UDP 是面向报文的
UDP 没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如 IP 电话,实时视频会议等)

4)、每一条 TCP 连接只能是点到点的;UDP 支持一对一,一对多,多对一和多对多的交互通信

5)、TCP 首部开销 20 字节;UDP 的首部开销小,只有 8 个字节

6)、TCP 的逻辑通信信道是全双工的可靠信道,UDP 则是不可靠信道

2、TCP 协议如何保证可靠传输?

https://www.cnblogs.com/xiaokang01/p/10033267.html

3、TCP 的握手、挥手机制?

TCP 的握手机制:

TCP 的挥手机制:

详情参考文章:
https://blog.csdn.net/qzcsu/article/details/72861891

4、TCP 的粘包 / 拆包原因及其解决方法是什么?

为什么会发生 TCP 粘包、拆包?
发生 TCP 粘包、拆包主要是由于下面一些原因:

1). 应用程序写入的数据大于套接字缓冲区大小,这将会发生拆包。

2). 应用程序写入数据小于套接字缓冲区大小,网卡将应用多次写入的数据发送到网络上,这将会发生粘包。

3). 进行 MSS(最大报文长度)大小的 TCP 分段,当 TCP 报文长度 -TCP 头部长度 >MSS 的时候将发生拆包。

4). 接收方法不及时读取套接字缓冲区数据,这将发生粘包。

粘包、拆包解决办法:

TCP 本身是面向流的,作为网络服务器,如何从这源源不断涌来的数据流中拆分出或者合并出有意义的信息呢?通常会有以下一些常用的方法:

1)、发送端给每个数据包添加包首部,首部中应该至少包含数据包的长度,这样接收端在接收到数据后,通过读取包首部的长度字段,便知道每一个数据包的实际长度了。

2)、发送端将每个数据包封装为固定长度(不够的可以通过补 0 填充),这样接收端每次从接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来。

3)、可以在数据包之间设置边界,如添加特殊符号,这样,接收端通过这个边界就可以将不同的数据包拆分开。

详情参考文章:https://www.cnblogs.com/panch…

5、Netty 的粘包 / 拆包是怎么处理的,有哪些实现?

对于粘包和拆包问题,常见的解决方案有四种:

1)、客户端在发送数据包的时候,每个包都固定长度,比如 1024 个字节大小,如果客户端发送的数据长度不足 1024 个字节,则通过补充空格的方式补全到指定长度;Netty 提供的 FixedLengthFrameDecoder

2)、客户端在每个包的末尾使用固定的分隔符,例如 rn,如果一个包被拆分了,则等待下一个包发送过来之后找到其中的 rn,然后对其拆分后的头部部分与前一个包的剩余部分进行合并,这样就得到了一个完整的包;Netty 提供 LineBasedFrameDecoder 与 DelimiterBasedFrameDecoder

3)、将消息分为头部和消息体,在头部中保存有当前整个消息的长度,只有在读取到足够长度的消息之后才算是读到了一个完整的消息;Netyy 提供了 LengthFieldBasedFrameDecoder 与 LengthFieldPrepender

4)、通过自定义协议进行粘包和拆包的处理。Netty 提供了通过实现 MessageToByteEncoder 和 ByteToMessageDecoder 来实现

更详细请阅读文章:https://www.cnblogs.com/AIPAO…

6、同步与异步、阻塞与非阻塞的区别?

简单点理解就是:

1). 同步,就是我调用一个功能,该功能没有结束前,我死等结果。

2). 异步,就是我调用一个功能,不需要知道该功能结果,该功能有结果后通知我(回调通知)

3). 阻塞,就是调用我(函数),我(函数)没有接收完数据或者没有得到结果之前,我不会返回。

4). 非阻塞,就是调用我(函数),我(函数)立即返回,通过 select 通知调用者

同步 IO 和异步 IO 的区别就在于:数据拷贝的时候进程是否阻塞

阻塞 IO 和非阻塞 IO 的区别就在于:应用程序的调用是否立即返回

7、说说网络 IO 模型?

8、BIO、NIO、AIO 分别是什么?

BIO: 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。BIO 方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4 以前的唯一选择,但程序直观简单易理解。

NIO: 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有 I / O 请求时才启动一个线程进行处理。NIO 方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4 开始支持。

AIO: 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的 I / O 请求都是由 OS 先完成了再通知服务器应用去启动线程进行处理.AIO 方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用 OS 参与并发操作,编程比较复杂,JDK7 开始支持。

9、select、poll、epoll 的机制及其区别?

1). 单个进程打开的文件描述符(fd 文件句柄)不一致

select:有最大连接数限制数为 1024,单个进程所能打开的最大连接数由 FD_ZETSIZE 宏定义。

poll:poll 本质上与 select 没有区别,但是它没有最大连接数的限制,原因是它是基于链表来存储的。

epoll:虽然连接有上限,但是很大,1G 内存的机器可以打开 10 万左右的连接,以此类推。

2). 监听 Socket 的方式不一致

select:轮询的方式,一个一个的 socket 检查过去,发现有 socket 活跃时才进行处理,当线性 socket 增多时,轮询的速度将会变得很慢,造成线性造成性能下降问题。

poll:对 select 稍微进行了优化,只是修改了文件描述符,但是监听 socket 的方式还是轮询。

expoll:epoll 内核中实现是根据每个 fd 上的 callback 函数来实现的,只有活跃的 socket 才会主动调用 callback,通知 expoll 来处理这个 socket。(会将连接的 socket 注册到 epoll 中, 相当于 socket 的花名册, 如果有一个 socket 活跃了, 会回调一个函数, 通知 epoll, 赶紧过来处理)

3). 内存空间拷贝方式(消息传递方式)不一致

select:内核想将消息传递到用户态,需要将数据从内核态拷贝到用户态, 这个过程非常的耗时

poll:同上

epoll:epoll 的内核和用户空间共享一块内存,因此内存态数据和用户态数据是共享的

select、poll、epoll 时间复杂度分别是:O(n)、O(n)、O(1)

10、说说你对 Netty 的了解?

这个没用过的话,就不要死撑了。用过的估计不用找了吧。

11、Netty 跟 Java NIO 有什么不同,为什么不直接使用 JDK NIO 类库?

说说 NIO 有什么缺点吧:

NIO 的类库和 API 还是有点复杂,比如 Buffer 的使用
Selector 编写复杂,如果对某个事件注册后,业务代码过于耦合
需要了解很多多线程的知识,熟悉网络编程
面对断连重连、保丢失、粘包等,处理复杂
NIO 存在 BUG,根据网上言论说是 selector 空轮训导致 CPU 飙升,具体有兴趣的可以看看 JDK 的官网

Netty 主要的优点有:

框架设计优雅,底层模型随意切换适应不同的网络协议要求

提供很多标准的协议、安全、编码解码的支持

解决了很多 NIO 不易用的问题

社区更为活跃,在很多开源框架中使用,如 Dubbo、RocketMQ、Spark 等

底层核心有:Zero-Copy-Capable
Buffer,非常易用的灵拷贝 Buffer(这个内容很有意思,稍后专门来说);统一的 API;标准可扩展的时间模型

传输方面的支持有:管道通信(具体不知道干啥的,还请老司机指教);Http 隧道;TCP 与 UDP

协议方面的支持有:基于原始文本和二进制的协议;解压缩;大文件传输;流媒体传输;protobuf 编解码;安全认证;http 和 websocket

总之提供了很多现成的功能可以直接供开发者使用。

12、Netty 组件有哪些,分别有什么关联?

Channel —-Socket

EventLoop —- 控制流,多线程处理,并发;

ChannelHandler 和 ChannelPipeline

Bootstrap 和 ServerBootstrap

更多详情可以阅读:https://blog.csdn.net/summerZ…

13、说说 Netty 的执行流程?

1)、创建 ServerBootStrap 实例

2)、设置并绑定 Reactor 线程池:EventLoopGroup,EventLoop 就是处理所有注册到本线程的 Selector 上面的 Channel

3)、设置并绑定服务端的 channel

4)、5)、创建处理网络事件的 ChannelPipeline 和 handler,网络时间以流的形式在其中流转,handler 完成多数的功能定制:比如编解码 SSl 安全认证

6)、绑定并启动监听端口

7)、当轮训到准备就绪的 channel 后,由 Reactor 线程:NioEventLoop 执行 pipline 中的方法,最终调度并执行 channelHandler

更多请参考文章:https://juejin.im/post/5bf8fb…

14、Netty 高性能体现在哪些方面?

https://www.infoq.cn/article/…

15、Netty 的线程模型是怎么样的?

Reactor 线程模型

Reactor 单线程模型
一个 NIO 线程 + 一个 accept 线程:

Reactor 多线程模型

Reactor 主从模型
主从 Reactor 多线程:多个 acceptor 的 NIO 线程池用于接受客户端的连接

Netty 可以基于如上三种模型进行灵活的配置。

总结

Netty 是建立在 NIO 基础之上,Netty 在 NIO 之上又提供了更高层次的抽象。在 Netty 里面,Accept 连接可以使用单独的线程池去处理,读写操作又是另外的线程池来处理。Accept 连接和读写操作也可以使用同一个线程池来进行处理。而请求处理逻辑既可以使用单独的线程池进行处理,也可以跟放在读写线程一块处理。线程池中的每一个线程都是 NIO 线程。用户可以根据实际情况进行组装,构造出满足系统需求的高性能并发模型。

16、Netty 的零拷贝提体现在哪里,与操作系统上的有什么区别?

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

  1. File.read(bytes)
  2. Socket.send(bytes)

这种方式需要四次数据拷贝和四次上下文切换:

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

零拷贝的概念明显上面的第二步和第三步是没有必要的,通过 java 的 FileChannel.transferTo 方法,可以避免上面两次多余的拷贝(当然这需要底层操作系统支持)

  1. 调用 transferTo, 数据从文件由 DMA 引擎拷贝到内核 read buffer
  2. 接着 DMA 从内核 read buffer 将数据拷贝到网卡接口 buffer 上面的两次操作都不需要 CPU 参与,所以就达到了零拷贝。

Netty 中的零拷贝主要体现在三个方面:

1、bytebufferNetty 发送和接收消息主要使用 bytebuffer,bytebuffer 使用对外内存(DirectMemory)直接进行 Socket 读写。原因:如果使用传统的堆内存进行 Socket 读写,JVM 会将堆内存 buffer 拷贝一份到直接内存中然后再写入 socket,多了一次缓冲区的内存拷贝。DirectMemory 中可以直接通过 DMA 发送到网卡接口

2、Composite Buffers 传统的 ByteBuffer,如果需要将两个 ByteBuffer 中的数据组合到一起,我们需要首先创建一个 size=size1+size2 大小的新的数组,然后将两个数组中的数据拷贝到新的数组中。但是使用 Netty 提供的组合 ByteBuf,就可以避免这样的操作,因为 CompositeByteBuf 并没有真正将多个 Buffer 组合起来,而是保存了它们的引用,从而避免了数据的拷贝,实现了零拷贝。

3、对于 FileChannel.transferTo 的使用 Netty 中使用了 FileChannel 的 transferTo 方法,该方法依赖于操作系统实现零拷贝。

17、Netty 的内存池是怎么实现的?

netty 内存池实现原理

netty 内存池可以分配堆内存和非堆内存(Direct 内存),内存分配的核心算法是类似的,从堆内存分配代码入手来学习整个内存池的原理。netty 框架处理 IO 事件时,使用 ByteBuf 承载数据。ByteBuf 的内存分配由 PooledByteBufAllocator 来执行,最终的内存分配工作会被委托给 PoolArena,堆内存分配的 PoolArena 实现是 HeapArena。

netty 通常被用于高并发系统,多线程竞争加锁会影响内存分配的效率,为了缓解高并发时的线程竞争,netty 允许使用者创建多个分配器(PoolArena)来分离线程竞争,提高内存分配效率。可通过 PooledByteBufAllocator 构造子中的 nHeapArena 参数来设置 PoolArena 的数量,或者直接取框架中的默认值,通过以下代码决定默认值,默认值根据 CPU 核心数、JVM 最大可用内存以及默认内存块(PoolChunk)大小等参数来计算这个默认值,计算逻辑是:

1)获取系统变量 io.netty.allocator.numHeapArenas,通过 System.setProperty(“io.netty.allocator.numHeapArenas”,xxx)或者增加 JVM 启动参数 -Dio.netty.allocator.numHeapArenas=xxx 设置,如果设置了值,把这个值当做 Arena 的个数。

2)如果没有设置 io.netty.allocator.numHeapArenas 系统变量,计算 CPU 核心数 * 2 和 JVM 最大可用内存 / 默认内存块大小 /2/3,取其中一个较少的值当做 PoolArena 的个数。

确定 PoolArena 个数之后框架会创建一个 PoolArena 数组,数组中所有的 PoolArena 都会用来执行内存分配。线程申请内存分配时,线程会在这个 PoolArena 数组中挑选一个当前被占用次数最少的 Arena 执行内存分配。

此外,netty 使用扩展的线程对象 FastThreadLocalThread 来优化 ThreadLocal 性能,具体的优化思路是:默认的 ThreadLocal 使用 ThreadLocalMap 存储线程局部变量,它的实现方式类似于 HashMap,需要计算 hashCode 定位到线程局部变量所在 Entry 的索引,而 FastThreadLocalThread 使用 FastThreadLocal 代替 ThreadLocal,FastThreadLocalThread 用一个数组来维护线程变量,每个 FastThreadLocal 维护一个 index,该 index 就是线程局部变量在数组中的位置,线程变量直接通过 index 访问无需计算 hashCode,FastThreadLocal 的优势是减少了 hashCode 的计算过程,虽然性能只会有轻微的提升,但在高并发系统中,即使只是轻微的提升也会成倍放大。

更多请阅读文章:http://baijiahao.baidu.com/s?…

18、Netty 的对象池是怎么实现的?

Netty 并没有使用第三方库实现对象池,而是自己实现了一个相对轻量的对象池。通过使用 threadLocal,避免了多线程下取数据时可能出现的线程安全问题,同时,为了实现多线程回收同一个实例,让每个线程对应一个队列,队列链接在 Stack 对象上形成链表,这样,就解决了多线程回收时的安全问题。同时,使用了软引用的 map 和 软引用的 thradl 也避免了内存泄漏。

更详细的可阅读文章:

https://www.jianshu.com/p/834…
https://www.cnblogs.com/hzmar…

19、在实际项目中,你们是怎么使用 Netty 的?

没用过。

20、使用过 Netty 遇到过什么问题?

(1)创建两个 NioEventLoopGroup, 用于逻辑隔离 NIO Acceptor 和 NIO I/ O 线程

(2)尽量不要在 ChannelHandler 中启动用户线程(解码后用于将 POJO 消息派发到后端业务线程的除外)

(3)解码要放在 NIO 线程调用的解码 Handler 中进行, 不要切换到用户线程完成消息的解码.

(4)如果业务逻辑操作非常简单(纯内存操作), 没有复杂的业务逻辑计算, 也可能会导致线程被阻塞的磁盘操作, 数据库操作, 网络操作等, 可以直接在 NIO 线程上完成业务逻辑编排, 不需要切换到用户线程.

(5)如果业务逻辑复杂, 不要在 NIO 线程上完成, 建议将解码后的 POJO 消息封装成任务, 派发到业务线程池中由业务线程执行, 以保证 NIO 线程尽快释放, 处理其它 I / O 操作.

(6)可能导致阻塞的操作, 数据库操作, 第三方服务调用, 中间件服务调用, 同步获取锁,Sleep 等

(7)Sharable 注解的 ChannelHandler 要慎用

(8)避免将 ChannelHandler 加入到不同的 ChannelPipeline 中, 会出现并发问题.

从上面的随便挑一个吹水就行。

21、netty 的线程模型,netty 如何基于 reactor 模型上实现的。

这个网上很多了,就不说了。
https://www.cnblogs.com/codin…

23、netty 的 fashwheeltimer 的用法,实现原理,是否出现过调用不够准时,怎么解决。

https://www.cnblogs.com/eryua…

24、netty 的心跳处理在弱网下怎么办。

https://blog.csdn.net/z691837…

25、netty 的通讯协议是什么样的。

https://www.cnblogs.com/54929…

正文完
 0