文章首发地址
Netty高性能的三个主题
- I/O传输模型:用什么样的通道将数据发送给对方,是BIO、NIO还是AIO,I/O传输模型在很大程度上决定了框架的性能。
- 数据协议:采用什么样的通信协议,是HTTP还是内部私有协议。协议的选择不同,性能模型也就不同。一般来说内部私有协议比公有协议的性能更高。
- 线程模型:线程模型涉及如何读取数据包,读取之后的编解码在哪个线程中进行,编解码后的消息如何派发等方面。线程模型设计得不同,对性能也会产生非常大的影响。
Netty高性能之核心法宝
异步非阻塞通信
Netty就是一个满足高性能、高并发的网络通信框架。Netty底层是采用Reactor线程模型来设计和实现的,先来看Netty服务端API的通信步骤,其序列图如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
Netty客户端API的通信步骤:
零拷贝
- Netty接收和发送ByteBuffer采用DirectBuffer,使用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝。如果使用传统的堆存(Heap Buffer)进行Socket读写,那么JVM会将堆存拷贝一份到直接内存中,然后才写入Socket。相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。
- Netty提供了组合Buffer对象,可以聚合多个ByteBuffer对象,用户可以像操作一个Buffer那样方便地对组合Buffer进行操作,避免了传统的通过内存拷贝的方式将几个小Buffer合并成一个大Buffer的烦琐操作。
- Netty中文件传输采用了transferTo()方法,它可以直接将文件缓冲区的数据发送到目标Channel,避免了传统通过循环write()方式导致的内存拷贝问题。
内存池
随着JVM和JIT(Just-In-Time)即时编译技术的发展,对象的分配和回收已然是一个非常轻量级的工作。但是对于缓冲区来说还有些特殊,尤其是对于堆外直接内存的分配和回收,是一种耗时的操作。为了尽量重复利用缓冲区内存,Netty设计了一套基于内存池的缓冲区重用机制。
高效的Reactor线程模型
常用的Reactor线程模型有三种,分别如下:
- Reactor单线程模型。
- Reactor多线程模型。
- 主从Reactor多线程模型。
无锁化的串行设计理念
在大多数应用场景下,并行多线程处理可以提升系统的并发性能。但是,如果对共享资源的并发访问处理不当,就会造成严重的锁竞争,最终导致系统性能的下降。为了尽可能避免锁竞争带来的性能损耗,可以通过串行化设计来避免多线程竞争和同步锁,即消息的处理尽可能在同一个线程内完成,不进行线程切换。
高效的并发编程
Netty的高效并发编程主要体现在如下几点:
- volatile关键字的大量且正确的使用。
- CAS和原子类的广泛使用。
- 线程安全容器的使用。
对高性能的序列化框架的支持
影响序列化性能的关键因素总结如下:
- 序列化后的码流大小(网络带宽的占用)。
- 序列化/反序列化的性能(CPU资源占用)。
- 是否支持跨语言(异构系统的对接和开发语言切换)。
Netty默认提供了对Google Protobuf的支持,用户也可以通过扩展Netty的编解码接口接入其他高性能的序列化框架进行编解码,例如Thrift的压缩二进制编解码框架。
灵活的TCP参数配置能力
合理设置TCP参数在某些场景下对性能的提升具有显著的效果,例如SO_RCVBUF和SO_SNDBUF。如果设置不当,对性能的影响也是非常大的。