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

Ocelot API网关大文件并发上传引发内存溢出,如何让UploadVideo端点绕过网关直接访问后端主机?

解决Ocelot网关大文件上传内存溢出及端点绕开方案

这个问题我之前碰到过一模一样的场景,核心原因是Ocelot默认会把整个请求体加载到内存中处理,当多个200MB的视频并发上传时,很容易触发OutOfMemoryException——哪怕临时升级服务器内存,用户量上来后还是会重复踩坑。要让UploadVideo端点绕开Ocelot直接访问后端,有几个靠谱的方案:

方案1:在反向代理层(Nginx/IIS)做路径分流

最推荐的方式是在Ocelot前面的反向代理服务器上配置规则,把特定的上传路径直接转发到后端服务,其他请求继续走Ocelot网关。这种方式不需要改动网关代码,性能也最稳定。

以Nginx为例

修改Nginx配置文件,添加一个优先级更高的location块专门匹配上传路径:

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

    # 直接转发上传请求到后端视频服务
    location /v1/Video/UploadVideo {
        proxy_pass http://your-backend-video-server:port;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        # 关键配置:禁用请求体缓存,直接流式转发
        proxy_request_buffering off;
    }

    # 其他所有请求转发到Ocelot网关
    location / {
        proxy_pass http://your-ocelot-server:port;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

proxy_request_buffering off能让Nginx跳过本地缓存请求体的步骤,直接把客户端的上传流转发给后端,彻底避免内存占用问题。

以IIS为例

利用IIS的URL重写模块添加分流规则:

  1. 打开IIS管理器,找到网关站点,进入「URL重写」功能
  2. 点击「添加规则」,选择「反向代理规则」
  3. 设置匹配URL为^v1/Video/UploadVideo$,后端服务器地址填你的视频服务地址
  4. 确保这个规则的优先级高于指向Ocelot的规则(可以通过规则排序调整)

方案2:自定义Ocelot中间件拦截并转发

如果不想改动反向代理层,可以在Ocelot项目中添加一个自定义中间件,在请求进入Ocelot核心逻辑之前,判断路径是否为上传端点,直接转发到后端。

Startup.csConfigure方法中,把这个中间件放在Ocelot中间件之前:

app.Use(async (context, next) =>
{
    var requestPath = context.Request.Path.Value;
    // 匹配上传端点的POST请求
    if (requestPath.Equals("/v1/Video/UploadVideo", StringComparison.OrdinalIgnoreCase) 
        && context.Request.Method.Equals("POST", StringComparison.OrdinalIgnoreCase))
    {
        // 直接转发到后端视频服务
        using var httpClient = new HttpClient();
        var forwardRequest = new HttpRequestMessage(HttpMethod.Post, "http://your-backend-video-server:port/v1/Video/UploadVideo");
        
        // 流式转发请求体,完全不占用本地内存
        forwardRequest.Content = new StreamContent(context.Request.Body);
        // 复制原请求的所有头信息
        foreach (var header in context.Request.Headers)
        {
            forwardRequest.Content.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
        }
        
        // 异步发送请求,只等待响应头返回
        var backendResponse = await httpClient.SendAsync(forwardRequest, HttpCompletionOption.ResponseHeadersRead);
        
        // 把后端响应原样返回给客户端
        context.Response.StatusCode = (int)backendResponse.StatusCode;
        foreach (var header in backendResponse.Headers)
        {
            context.Response.Headers.TryAdd(header.Key, header.Value.ToArray());
        }
        foreach (var header in backendResponse.Content.Headers)
        {
            context.Response.Headers.TryAdd(header.Key, header.Value.ToArray());
        }
        
        await backendResponse.Content.CopyToAsync(context.Response.Body);
        return;
    }
    
    // 非上传请求,继续走Ocelot的网关逻辑
    await next();
});

// 原有Ocelot中间件
app.UseOcelot().Wait();

这个方案的核心是流式传输,全程不会把视频文件读到内存里,完美解决内存溢出问题。

备选方案:优化Ocelot大文件处理(不推荐)

如果一定要通过Ocelot处理上传请求,可以尝试开启流式传输支持,但实际测试中,对于超大文件并发场景,稳定性不如直接绕开网关:
ocelot.json的对应路由中添加配置:

{
  "Routes": [
    {
      "DownstreamPathTemplate": "/v1/Video/UploadVideo",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "your-backend-video-server",
          "Port": port
        }
      ],
      "UpstreamPathTemplate": "/v1/Video/UploadVideo",
      "UpstreamHttpMethod": [ "POST" ],
      "RequestBuilderOptions": {
        "DisableRequestBodyBuffering": true
      }
    }
  ]
}

DisableRequestBodyBuffering会让Ocelot尝试跳过请求体缓存,但部分Ocelot版本对这个配置的支持不够完善,大并发下还是可能出现内存问题。


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

火山引擎 最新活动