You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Express JS高CPU占用求助:m3u8直播50+用户时CPU飙升

高并发m3u8直播转发CPU占用过高的优化方案

针对你的场景,先梳理问题根源,再给出可落地的优化方案:

问题根源

  1. 同步IO阻塞事件循环
    最初的方案中fs.writeFileSync是同步阻塞操作,每个请求都会卡住Node.js的事件循环。当用户数达到50+时,大量同步操作堆积,事件循环无法及时处理其他请求,直接导致CPU占用率暴涨。而且m3u8内容完全不需要落地磁盘,流式转发即可规避磁盘IO和同步阻塞的双重开销。

  2. HTTP连接未复用
    即使使用request(newurl).pipe(res)的流式转发,request库默认的连接池配置有限,且未充分启用TCP keep-alive。每个m3u8索引和ts片段请求都会新建与原服务器的TCP连接,频繁的三次握手、四次挥手会消耗大量CPU资源。

  3. 重定向的隐性负载
    直接重定向到原URL时,你的服务器仍需处理每个用户的重定向请求(返回302响应),加上客户端会频繁拉取m3u8和ts片段,总请求量依然很高。如果客户端未启用连接复用,大量短连接的建立也会推高CPU占用。

优化方案

1. 移除同步文件操作,改用纯流式转发

替换落地磁盘的逻辑,直接将原服务器的响应流式转发给客户端,同时启用TCP连接复用:

const http = require('http');
const { URL } = require('url');

// 复用连接的HTTP Agent
const keepAliveAgent = new http.Agent({
  keepAlive: true,
  maxSockets: 100, // 根据并发量调整
  keepAliveMsecs: 30000
});

function proxyLiveResource(req, res, targetUrl) {
  const urlObj = new URL(targetUrl);
  const proxyOptions = {
    hostname: urlObj.hostname,
    port: urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80),
    path: urlObj.pathname + urlObj.search,
    method: req.method,
    headers: {
      ...req.headers,
      host: urlObj.hostname, // 修正Host头,确保原服务器正确识别
      'Connection': 'keep-alive'
    },
    agent: keepAliveAgent
  };

  const proxyReq = http.request(proxyOptions, (proxyRes) => {
    // 透传原响应头
    res.writeHead(proxyRes.statusCode, proxyRes.headers);
    // 流式转发响应体
    proxyRes.pipe(res);
  });

  proxyReq.on('error', (err) => {
    res.statusCode = 500;
    res.end('Proxy error');
  });

  // 将客户端请求体转发给原服务器(如果是POST等有请求体的方法)
  req.pipe(proxyReq);
}

2. 缓存m3u8索引文件

m3u8索引更新频率较低(通常3-10秒一次),短期缓存可避免重复请求原服务器:

let cachedM3U8 = '';
let cacheExpiry = 0;
const CACHE_TTL = 5000; // 缓存5秒,根据实际更新频率调整

async function getCachedM3U8(originalUrl) {
  const now = Date.now();
  if (now < cacheExpiry) return cachedM3U8;

  const response = await fetch(originalUrl);
  if (!response.ok) throw new Error('Failed to fetch m3u8');
  
  cachedM3U8 = await response.text();
  cacheExpiry = now + CACHE_TTL;
  return cachedM3U8;
}

// 在请求处理中使用
app.get('/live/channel1.m3u8', async (req, res) => {
  try {
    const m3u8Content = await getCachedM3U8('https://original-server/channel1.m3u8');
    // 如果原m3u8中的ts是相对路径,替换为你的转发域名
    const processedContent = m3u8Content.replace(/(\/.*\.ts)/g, `https://your-domain.com/live$1`);
    res.setHeader('Content-Type', 'application/vnd.apple.mpegurl');
    res.end(processedContent);
  } catch (err) {
    res.statusCode = 500;
    res.end('Failed to load live stream');
  }
});

3. 用Nginx做反向代理(高并发场景首选)

Node.js适合处理业务逻辑,但Nginx在静态资源转发上性能远超Node.js。可以让Node.js处理登录权限校验,将m3u8/ts请求交给Nginx代理:

server {
  listen 80;
  server_name your-domain.com;

  # 权限校验接口交给Node.js
  location /api/check-auth {
    proxy_pass http://localhost:3000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
  }

  # 直播资源代理
  location /live {
    # 校验用户权限(可选,通过Cookie或请求头)
    if ($http_cookie !~* "auth_token=.*") {
      return 403;
    }

    proxy_pass http://original-m3u8-server.com;
    proxy_set_header Host original-m3u8-server.com;
    proxy_http_version 1.1;
    proxy_set_header Connection ""; # 启用keep-alive
    # 缓存m3u8索引
    proxy_cache_path /var/cache/nginx/m3u8 levels=1:2 keys_zone=m3u8_cache:10m max_size=10g inactive=60m;
    proxy_cache m3u8_cache;
    proxy_cache_valid 200 5s;
    # ts片段不缓存(避免旧片段被复用)
    proxy_cache_bypass $request_uri ~* \.ts$;
  }
}

Nginx的连接池和缓存机制能大幅降低CPU占用,即使数百用户并发也能稳定运行。

4. 其他优化点

  • 升级Node.js版本到最新稳定版,V8引擎的性能优化能降低CPU开销
  • 禁用不必要的日志输出,日志IO也会占用CPU资源
  • 调整视频播放器的缓冲策略,减少过于频繁的请求

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 18:50:26