Netty-架构之道

本文深入探讨了Netty的架构设计,包括Reactor通信调度层、ChannelPipeline职责链以及无锁化设计。Netty通过I/O多路复用实现异步非阻塞通信,提高系统效率,并采用内存池和引用计数器优化内存管理。此外,还介绍了心跳检测、读写空闲超时等机制确保连接可靠性,并讨论了流量整形以防止下游系统过载。Netty的高性能体现在减少网络传输、序列化性能和线程模型优化等方面。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  1. Netty逻辑架构
    在这里插入图片描述
  • Reactor通信调度层由一系列辅助类完成,包括Reactor线程NioEventLoop及其父类,NioSocketChannel/NioServerSocketChannel,ByteBuf等。主要的职责是监听网络的读写和连接操作,负责将网络层的数据读取到内存缓冲区中,然后触发各种网络事件,如连接创建,读写事件,再将这些事件触发到PipeLine中,由Pipeline管理的职责链进行处理
  • 职责链ChannelPipeline负责事件在职责链中的有序传播,同时负责动态编排职责链。职责链选择监听和处理自己关心的事件,可以拦截处理后向前向后传播事件。ChannelPipeline中的不同的Handler节点的功能也不同,可以用于编解码,协议转换等。
  • 业务逻辑编排层通常由2类,一类是纯粹的业务逻辑编排,一类是其他的应用层协议插件,用于特定协议相关的会话和链路管理。

Java多线程编程在Netty中的应用

  1. Synchronized关键字可以保证在同一时刻只有一个线程可以执行某个方法或者代码块。同步的作用不仅是互斥,还有共享可变性,当某线程修改了可变数据并释放锁,其他线程可以获取修改的最新值。
public <T> B option(ChannelOption<T> option, T value) {
        if (option == null) {
            throw new NullPointerException("option");
        }
        if (value == null) {
            synchronized (options) {
                options.remove(option);
            }
        } else {
            synchronized (options) {
                options.put(option, value);
            }
        }
        return self();
    }
  1. volatile关键字:保证可见性和防止指令重排序。private volatile int ioRatio = 50;
  2. CAS和原子类:避免同步锁带来的并发访问性能降低的问题。
 private static final AtomicLongFieldUpdater<ChannelOutboundBuffer> TOTAL_PENDING_SIZE_UPDATER =
            AtomicLongFieldUpdater.newUpdater(ChannelOutboundBuffer.class, "totalPendingSize");
  1. 线程安全类:使用到ConcurrentLinkedQueue、线程池、线程安全队列等等。
  2. 读写锁:新增一个定时任务的时候使用了读锁,获取并删除过期任务时,由于要从迭代器中删除任务,使用了写锁。

高性能

  1. 传统的RPC调用性能差的三个问题

    • 网络传输方式问题。传统的RPC框架采用了同步阻塞I/O,当客户端的并发压力或者网络时延增大之后, I/O处理能力下降。
    • 序列化性能差。Java序列化无法跨语言使用、序列化后码流太大,占用额外资源、资源占用率高
    • 线程模型问题。采用 阻塞I/O导致每个TCP连接占用1个线程,当读写阻塞导致线程无法及时释放,导致系统性能下降 。
  2. I/O通信性能三原则

    • 传输:用什么样的通道发送数据给对方。
    • 协议:采用什么样的通信协议。
    • 线程:数据如何被读取?读取之后编解码在哪个线程进行,消息如何派发。
  3. 异步非阻塞通信:采用I/O多路复用技术,把多个I/O的阻塞复用到同一个select的阻塞上,从而使系统在单线程的情况下可以同时处理多个客户端请求,I/O多路复用的最大优势是系统开销小,系统不需要创建新的额外进程或者线程,也不需要维护这些进程和线程的运行,降低了系统的维护工作量,节省了系统资源。
    Netty的I/O线程NioEventLoop 聚合了多路复用Selector ,可以同时并发处理多个客户端Channel,读写操作是非阻塞的,提升I/O线程运行效率。同时采用异步通信,一个I/O线程可以并发处理N个客户端的连接和读写操作。

  4. 高效的Reactor模式:可以根据场景选择主从Reactor模式、单Reactor多线程模式或者单线程模式。

  5. 无锁化的设计:采用串行无锁化设计,在I/O线程内部进行串行操作,避免多线程竞争导致的性能下降。Netty的NioEventLoop 读取到消息之后,直接调用ChannelPipeline 的fireChannelRead(Object msg)方法,只要用户不切换线程,NioEventLoop一直 调用用户Handler。

  6. 高效的并发编程:使用了大量的并发编程类,线程安全容器,锁等。

  7. 零拷贝(三种实现)

    • Netty的接受和发送ByteBuffer采用的是直接内存代替堆内存,使用直接内存进行socket读写,不需要进行字节缓冲区的二次拷贝
    • CompositeByteBuf,它对外将多个ByteBuf封装成一个ByteBuf,提供统一封装后的接口。
    • 文件传输。文件传输类DefaultFileRegion通过transferTo方法将文件发送到目标Channel中。不需要通过循环拷贝的方式,提升传输性能,降低CPU和内存占用。
  8. 内存池提供基于内存池的缓冲区重用机制。支持通过内存池的方式循环利用ByteBuf,避免了频繁创建和销毁ByteBuf带来的性能损耗。

  9. **引用计数器:**通过引用计数器及时地申请释放不再被引用的对象,细粒度的内存管理降低了GC的频率,减少了频繁GC带来的时延增大和CPU损耗。
    10.**灵活的TCP参数配置:**可配置的I/O线程数、TCP参数等,为不同的用户场景提供定制的调优参数,满足不同的性能场景。

可靠性

  1. 客户端连接超时:指定连接时间,如果超时但服务端仍没有返回TCP握手应答,则关闭连接。如果在超时期限内完成,则取消连接超时定时任务。
  2. 通信对端强制关闭连接:由于TCP是全双工的,通信双方都要关闭和释放Socket句柄才不会发生句柄的泄漏。
  3. **心跳检测机制:**周期性地进行链路检测,检查链路的可用性,一旦发现问题可以及时关闭链路,重建TCP连接。
    * 读空闲超时机制:连续周期T没消息可读,触发超时Handler进行检测,如果连续N个周期仍没有读到心跳消息,主动关闭链路。
    * 写空闲超时机制:连续周期T没消息要发送,触发超时Handler进行检测,如果连续N个周期仍没有收到对方的心跳消息,主动关闭链路。
  4. 内存保护
    • 链路总数的控制:每条链路都包含接收和发送缓冲区,链路个数太多容易导致内存溢出:
    • 单个缓冲区的上限控制:防止非法长度或者消息过大导致内存溢出:
    • 缓冲区内存释放:防止因为缓冲区使用不当导致的内存泄露;
    • NIO消息发送队列的长度上限控制
    • 缓冲区内存泄露保护:内存申请、使用、释放的时候都会自动进行引用计数检测,防止内发使用内存。
    • 缓冲区内存溢出保护:采用容量预分配和根据协议消息长度创建缓冲区方式。
    • 流量整形:主动调整流量输出速率,防止上下游网元性能不匀衡导致下游网元被压垮,业务流程中断。防止由于通信模块接受消息过快,后端业务线程处理不及时导致撑死问题。
  5. 优雅停机: 当系统接收到退出指令后,首先标记系统处于退出状态,不再接收新的消息,然后将积压的消息处理完,最后调用资源回收接口将资源销毁,最后各线程退出执行

补充
Netty流量整形的原理 :对每次读取到的ByteBuf 可写字节数进行计算,获取当前的报文流量,然后与流量整形阈值对比。如果已经达到或者超过了阈值。则计算等待时间delay,将当前的ByteBuf放到定时任务Task中缓存,由定时任务线程池在延迟delay之后继续处理该ByteBuf。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值