Netty
Netty 支持的功能与特性
Netty 是一个异步、事件驱动的用来做高性能、高可靠性的网络应用框架。主要的优点有:
- 框架设计优雅,底层模型随意切换适应不同的网络协议要求。
- 提供很多标准的协议、安全、编码解码的支持。
- 解决了很多 NIO 不易用的问题。
- 社区更为活跃,在很多开源框架中使用,如 Dubbo、RocketMQ、Spark 等。
上图体现的主要是 Netty 支持的功能或者特性:
- 1.底层核心有:Zero-Copy-Capable Buffer,非常易用的灵拷贝 Buffer(这个内容很有意思,稍后专门来说);统一的 API;标准可扩展的时间模型
- 2.传输方面的支持有:管道通信(具体不知道干啥的,还请老司机指教);Http 隧道;TCP 与 UDP
- 3.协议方面的支持有:基于原始文本和二进制的协议;解压缩;大文件传输;流媒体传输;protobuf 编解码;安全认 证;http 和 websocket
Netty 采用 NIO 而非 AIO 的理由
1.Netty 不看重 Windows 上的使用,在 Linux 系统上,AIO 的底层实现仍使用 EPOLL,没有很好实现 AIO,因此在性 能上没有明显的优势,而且被 JDK 封装了一层不容易深度优化
2.Netty 整体架构是 reactor 模型, 而 AIO 是 proactor 模型, 混合在一起会非常混乱,把 AIO 也改造成 reactor 模型看起 来是把 epoll 绕个弯又绕回来
3.AIO还有个缺点是接收数据需要预先分配缓存, 而不是NIO那种需要接收时才需要分配缓存, 所以对连接数量非常大 但流量小的情况, 内存浪费很多
4.Linux 上 AIO 不够成熟,处理回调结果速度跟不到处理需求,造成处理速度有瓶颈(待验证)
通过使用 Netty(NIO 框架)相比于传统基于 Java 序列化+BIO(同步阻塞 IO)的通信框架,性能提升了 8 倍多。
传统 RPC 调用性能差的三宗罪
网络传输方式问题:传统的 RPC 框架或者基于 RMI 等方式的远程服务(过程)调用采用了同步阻塞 IO,当客户端的 并发压力或者网络时延增大之后,同步阻塞 IO 会由于频繁的 wait 导致 IO 线程经常性的阻塞,由于线程无法高效的工 作,IO 处理能力自然下降。下面,我们通过 BIO 通信模型图看下 BIO 通信的弊端:
BIO通信模型
采用 BIO 通信模型的服务端,通常由一个独立的 Acceptor 线程负责监听客户端的连接,接收到客户端连接之后为客户 端连接创建一个新的线程处理请求消息,处理完成之后,返回应答消息给客户端,线程销毁,这就是典型的一请求一 应答模型。该架构最大的问题就是不具备弹性伸缩能力,当并发访问量增加后,服务端的线程个数和并发访问数成线 性正比,由于线程是 JAVA 虚拟机非常宝贵的系统资源,当线程数膨胀之后,系统的性能急剧下降,随着并发量的继续增加,可能会发生句柄溢出、线程堆栈溢出等问题,并导致服务器最终宕机。
序列化方式问题:Java 序列化存在如下几个典型问题:
-
- Java 序列化机制是 Java 内部的一种对象编解码技术,无法跨语言使用;例如对于异构系统之间的对接,Java 序列化 后的码流需要能够通过其它语言反序列化成原始对象(副本),目前很难支持;
-
- 相比于其它开源的序列化框架,Java 序列化后的码流太大,无论是网络传输还是持久化到磁盘,都会导致额外的资 源占用;
-
- 序列化性能差(CPU 资源占用高)。 线程模型问题:由于采用同步阻塞 IO,这会导致每个 TCP 连接都占用 1 个线程,由于线程资源是 JVM 虚拟机非常宝 贵的资源,当 IO 读写阻塞导致线程无法及时释放时,会导致系统性能急剧下降,严重的甚至会导致虚拟机无法创建新 的线程。
高性能的三个主题
- 1、传输:用什么样的通道将数据发送给对方,BIO、NIO 或者 AIO,IO 模型在很大程度上决定了框架的性能。
- 2、协议:采用什么样的通信协议,HTTP 或者内部私有协议。协议的选择不同,性能模型也不同。相比于公有协议, 内部私有协议的性能通常可以被设计的更优。
- 3、 线程:数据报如何读取?读取之后的编解码在哪个线程进行,编解码后的消息如何派发,Reactor 线程模型的不同, 对性能的影响也非常大。