• 服务端NioSocketChannel泄漏案例


    导致NioSocketChannel泄漏的可能原因有两个。

    (1)代码有缺陷,HTTPS 客户端关闭连接之后,服务端没有正确关闭连接,多发生在netty做https服务端并使用http/1.1时。
    (2) 服务端负载比较重,客户端超时之后的断连和重连速度超过服务端关闭连接速度,导致服务端的NioSocketChannel发生积压。随着积压数的增加,导致占用的内存快速增加,频繁GC使得服务端处理更慢,积压更严重,最终导致OOM异常。

    明确方向之后,具体的定位策略有3点。

    (1)通过netstat命令查看服务端的端口连接状态,是不断地创建和回收连接,还是服务端没有关闭连接导致连接一直增长。
    (2)查看NioSocketChannel的状态,是全部没关闭,还是部分关闭、部分打开。
    (3)停止压测一段时间,观察连接数,以及服务端的内存占用情况,看服务端是否可以自动恢复。
    在压测过程中,动态采集HTTPS的连接状态,发现超时的连接被服务端关闭,如图18-3所示。
    接着通过OQL在内存堆栈中查询NioSocketChannel的连接状态,其中处于关闭状态的连接数为25,如图18-4所示。
    服务端尚未主动关闭的NioSocketChannel实例个数为5806,如图18-5所示。
    从OQL查询可以看出,内存中尚有被服务端关闭但是还没来得及被NioSocketChannel对象,证明客户端超时关闭连接后,服务端感知了连接关闭事件并主动关闭了连接。动恢复。停止压测观察服务端内存使用情况,如图18-6所示。
    经过上述分析得知,并不是由于服务端忘记关闭 NioSocketChannel导致内存泄漏的,而是由于服务端关闭 NioSocketChannel的速度没有各厂端按八迷度内寸以NioSocketChannel缓慢积压,当积压到一定数量,无法在新生代被GC,所以达到晋升阈值后被复制到老年代,引起老年代GC,最终导致OOM异常

    netty实现http方式的rpc框架,问题还原:

    客户端采用HTTP连接池的方式与服务端进行RPC调用,单个客户端连接池上限为200,客户端部署了30个实例,而服务端只部署了3个实例。在业务高峰期,每个服务端需要处理6000个HTTP连接, 服务端时延增大之后,导致客户端批量超时,超时之后客户端会关闭连接重新发起connect 操作,在某个瞬间,几千个HTTPS 连接同时发起SSL握手操作,由于服务端此时也处于高负荷运行状态,导致部分连接SSL握手失败或者超时,超时之后客户端继续重连,进一步加重服务端的处理压力,最终导致服务端来不及释放客户端关闭的连接,引起 NioSocketChannel大量积压,最终导致OOM异常
    客户端也没有流控机制,只要连接数不够用,就会一直创建连接,达到连接池配置的最大连接数。正是由于客户端和服务端都没有对高并发时大量的HTTPS 链路断连和重连进行保护,导致了服务端OOM异常,业务中断

    问题解决:

    基于Netty的Pipeline机制,可以对SSL握手成功、SSL连接关闭做切面拦截(类似于Spring的AOP机制,但是没采用反射机制,性能更高),通过流控切面接口,对HTTPS连接进行计数,根据计数器进行流控,服务端的流控算法如下。
    (1)获取流控阈值。
    (2)从全局上下文中获取当前的并发连接数,与流控阈值对比,如果小于流控阈值,则对当前的计数器进行原子自增,允许客户端连接。
    (3)如果等于或者大于流控阈值,则抛出流控异常给客户端。
    (4)SSL连接关闭时,获取上下文中的并发连接数,进行原子自减。
    (1)流控的 ChannelHandler声明为@ChannelHandler.Sharable,这样创建一个全局流控实例,就可以在所有的SSL 连接中共享。
    (2) 通过userEventTriggered方法拦截SsIHandshakeCompletionEvent和SslCloseCompletion-Event事件,在SSL握手成功和SSL 连接关闭时更新流控计数器
    (3)流控并不是仅针对ESTABLISHED状态的HTTP连接,而是针对所有状态的连接,因为客户端关闭连接,并不意味着服务端也同时关闭连接,只有触发SsCloseCompletion-Event事件时,服务端才真正关闭了NioSocketChannel,GC才会回收连接关联的内存。
    (4)流控ChannelHandler会被多个NioEventLoop线程调用,因此对于相关的计数器更新等操作,要保证并发安全性,避免使用全局锁,可以通过原子类等提升性能。
  • 相关阅读:
    去了家新公司,技术总监不让用 IntelliJ IDEA想离职了
    项目管理系统(Java+Web+MySQL)
    JDK动态代理与CGLIB动态代理
    JS 保留两位小数,不足用0补齐;js 实现对数字保留两位小数时 不足两位 自动补0;JavaScript保留两位小数,自动补零
    基于jeecgboot的主从表改造成抽屉式的字典操作模式
    Leetcode:【485. 最大连续 1 的个数】
    【工程应用十】基于十六角度量化的夹角余弦相似度模版匹配算法原理解析。
    Matlab数据导入代码(importdata函数允许加载不同格式的各种数据文件)
    【哈佛公开课】积极心理学笔记-06乐观主义(上)
    gd32f303在IAR下的printf串口助手打印+串口收发配置
  • 原文地址:https://blog.csdn.net/qq_34448345/article/details/127440027