You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Node.js SOCKS5代理单CPU轮流100%占用问题排查求助

排查Node.js SOCKS5代理长期运行后CPU占满问题的思路

我之前也碰到过类似的长期运行Node.js服务CPU突然飙满的情况,结合你的strace输出、性能分析数据和代码场景,给你几个针对性的排查方向:

1. 先揪出epoll_pwait异常循环的根源

你的strace里epoll_pwait占了99.76%的CPU时间,调用次数还特别多——这是典型的事件循环高频空转信号,大概率和未正确清理的套接字资源有关:

  • 检查TCP/UDP套接字的关闭逻辑:不管是客户端断开、代理目标断开,还是请求处理失败,都要确保调用socket.destroy()或者socket.end()彻底释放资源。尤其是UDP套接字,因为它是无连接的,很容易出现绑定后忘记释放的情况。
  • 问题复现时用ss -tulnp | grep node查看套接字状态:看看有没有大量CLOSE_WAIT/TIME_WAIT的TCP连接,或者一堆没关闭的UDP套接字,这些都是事件循环的“隐形负担”。

2. 排查Cluster模式下的子进程资源泄漏

你用Cluster.fork()创建了多实例,要考虑子进程是否存在资源累积的问题:

  • 检查子进程里的全局变量:比如有没有用数组、Map存储请求上下文,却在请求结束后没清理?哪怕内存占用看起来正常,小对象的持续累积也可能触发事件循环的高频调度。
  • 手动测试子进程:问题出现时,kill掉一个CPU占满的子进程,如果CPU立刻回落,说明这个子进程有泄漏,可以单独对它做针对性分析(比如用node --inspect远程调试)。

3. 深入Node.js C++层定位问题

你的性能分析显示91.1%的时间花在C++层,syscall占了81%的ticks,说明问题出在原生IO处理部分:

  • 开启事件追踪启动服务:用node --trace-events-enabled --trace-event-categories=node.async_hooks,node.net,node.dgram启动,收集异步钩子和网络事件日志,长期运行后查看是否有大量未销毁的async资源(比如套接字句柄)。
  • perf做系统级分析:运行perf record -g -p <出问题的node进程PID>采样几分钟,再用perf report看调用栈,能直接定位到C++层里哪个函数在疯狂占用CPU,比如net模块的某个处理逻辑。

4. 检查SOCKS5协议处理的细节漏洞

SOCKS5的TCP转发和UDP关联逻辑很容易藏坑,有些错误处理不当会导致无限循环:

  • 验证UDP关联的处理:客户端请求UDP关联后,你是否正确处理了数据包的转发和响应?有没有可能在数据包解析或转发逻辑里出现了死循环?
  • 检查认证阶段的错误分支:如果客户端发送无效的认证请求,你是否正确关闭连接并释放所有相关资源?有没有某个错误分支里漏了清理套接字,导致事件循环一直处理这个无效的套接字事件?

5. 临时缓解方案

在找到根本原因前,可以先给Cluster加个自动重启机制:

const cluster = require('cluster');
const os = require('os');

if (cluster.isPrimary) {
  for (let i = 0; i < os.cpus().length; i++) {
    cluster.fork();
  }

  // 子进程退出时自动重启
  cluster.on('exit', (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} died, restarting...`);
    cluster.fork();
  });

  // 可选:定期检测子进程CPU占用,过高则主动杀死
  setInterval(() => {
    for (const worker of Object.values(cluster.workers)) {
      worker.process.send({ type: 'checkCPU' });
    }
  }, 60000);
} else {
  // 子进程逻辑:监听CPU检查请求,超过阈值则退出
  process.on('message', (msg) => {
    if (msg.type === 'checkCPU') {
      const usage = process.cpuUsage();
      const cpuPercent = (usage.user + usage.system) / 1000 / 60; // 计算最近60秒的CPU占比
      if (cpuPercent > 90) {
        process.exit(1);
      }
    }
  });

  // 启动你的SOCKS5代理服务器
}

内容的提问来源于stack exchange,提问作者Koray Gocmen

火山引擎 最新活动