介绍一下 Netty 使用的线程模型?
答:
Netty 主要基于主从 Reactor 多线程模型,其中主从 Reactor 多线程模型将 Reactor 分为两部分:
- mainReactor:监听 Server Socket,用来处理网络 IO 连接建立操作,将建立的 SocketChannel 指定注册给 subReactor
- subReactor:和建立起来的 socket 做数据交互和业务处理操作
因为客户端的连接数量相对来说比较少,而数据的读和写会比较多一点,使用 mainReactor 只接受客户端连接,由其他线程 subReactor 负责读和写,将业务处理剥离出,让线程池来处理,降低了 Reactor 的性能开销
扩展:单 Reactor 单线程模型、单 Reactor 多线程模型
- 单 Reactor 单线程模型
通过 1 个线程负责客户端连接、网络数据的读写、业务处理缓存 Redis 就是单 Reactor 单线程模型
- 单 Reactor 多线程模型
通过 1 个线程负责客户端的连接、网络数据的读写,将业务处理剥离出去,通过线程池来进行处理
三种 Reactor 模型的优缺点:
- 单 Reactor 单线程模型是单线程进行业务处理,当负载过重时,处理速度将会变慢,影响系统性能,因此引出单 Reactor 多线程模型
- 单 Reactor 多线程模型时多个线程处理业务,业务处理速度上来了,但是单 Reactor 承担了所有时间的监听和响应,可能存在性能问题。当有数百万客户端进行连接或者服务端需要对客户端握手进行安全认证,认证本身非常消耗性能,因此出现了主从 Reactor 多线程模型
- 主从 Reactor 多线程模型中 1 个主 Reactor 只用来处理网络 IO 的连接建立操作,而对于接入认证、IP 黑白名单过滤、握手等操作由从 Reactor 进行处理,这样进一步提升性能,在主从 Reactor 多线程模型中,从 Reactor 有多个,可以与 CPU 个数相同
TCP 粘包、拆包是什么?如何解决?
答:
TCP本身的机制决定了一定会有粘包、拆包,因为 TCP 传输协议时基于数据流传输的,而流化的数据没有界限,因此 TCP 作为传输层协议并不了解上层业务数据的具体含义,会根据 TCP 缓冲区的实际情况进行数据包的划分,所以业务上认为的一个完整的包,可能被 TCP 拆成多个包或者把多个小的包封装成一个大的包进行发送。
产生原因:
- 粘包:客户端发送的包的大小比socket的缓存小或者接收方读取socket缓存不及时,因此多个包一起发送了
- 拆包:客户端发送的包的大小比socket的缓存大或者发送的数据大于协议的MTU(最大传输单元)必须拆包,那么这个包就被拆分成了多个包进行发送
解决方法:
有三种方式:
- 通过指定分隔符来进行分割
- 通过指定固定长度来进行分割
- 上边两种方式灵活性不好,因此常用的是通过指定接收数据的长度来解决,也就是
LengthFieldBasedFrameDecoder()
这个类