.NET Core 3.1接收大JSON请求触发OutOfMemoryException如何修复
异常根因
该OutOfMemoryException由ASP.NET Core默认的请求处理逻辑触发:默认System.Text.Json输入格式化器会将完整1.21GB请求体全部加载到内存后再执行反序列化,你的JSON结构中仅包含base64编码的二进制内容,base64本身比原始二进制大33%,反序列化时还需要额外分配字节数组存储解码后的内容,单请求内存峰值会超过2GB,超过Kubernetes Pod配置的内存阈值后就会触发OOM。
可行修复方案
1. 跳过默认模型绑定,流式处理请求体
默认BodyModelBinder会自动缓冲全量请求体,你可以关闭自动绑定,直接从Request.Body流逐块解析数据,避免一次性加载全量内容:
[HttpPost] [DisableRequestSizeLimit] // 可按需替换为指定大小的[RequestSizeLimit] public async Task<IActionResult> ProcessLargePayload() { // 关闭Kestrel默认的全量请求缓冲,仅缓冲超出阈值的部分到磁盘 Request.EnableBuffering(bufferThreshold: 1024 * 1024, bufferLimit: null); // 流式解析JSON,不加载全量内容到内存 using var jsonDoc = await JsonDocument.ParseAsync(Request.Body, new JsonDocumentOptions { MaxDepth = 4, AllowTrailingCommas = false }); if (jsonDoc.RootElement.TryGetProperty("base64", out var base64Prop)) { // 直接读取base64内容为流,不要调用GetString()加载全量字符串 using var base64Stream = base64Prop.GetStreamFromBase64(); // 直接将流写入目标存储(文件/对象存储/数据库等),无需在内存驻留全量数据 using var targetStream = File.Create("output_binary.dat"); await base64Stream.CopyToAsync(targetStream, bufferSize: 128 * 1024); } return Ok(); }
注:GetStreamFromBase64方法需要.NET 5及以上版本支持,低版本可自行实现base64逐块解码逻辑
2. 调整服务端配置
在Program.cs中修改Kestrel和JSON序列化的默认配置,适配大请求场景:
// 配置Kestrel最大请求体大小 builder.WebHost.ConfigureKestrel(opt => { opt.Limits.MaxRequestBodySize = 2 * 1024 * 1024 * 1024L; // 设为2GB,可按需调整 }); // 调整JSON序列化选项降低内存占用 builder.Services.AddControllers() .AddJsonOptions(opt => { opt.JsonSerializerOptions.MaxDepth = 4; opt.JsonSerializerOptions.DefaultBufferSize = 128 * 1024; });
3. 调整Kubernetes资源配置
提高Pod的内存配额,避免被K8s OOM Killer主动杀掉:
resources: requests: memory: "2Gi" cpu: "1" limits: memory: "4Gi" cpu: "2"
建议将Pod配置为Guaranteed QoS等级(requests和limits值完全一致),降低集群资源紧张时Pod被优先回收的概率。
4. 长期架构优化
如果业务允许,优先修改交互协议:不要用JSON承载超大二进制内容,改为调用方直接将二进制流上传到对象存储(如MinIO、阿里云OSS等),仅将存储地址回调给你的API,从根本上避免大请求体带来的性能和稳定性问题。如果必须用JSON传输,也可以改为分块上传,单分片大小控制在100MB以内,后端完成分片拼接。
内容的提问来源于stack exchange,提问作者Lalit Rajput
相关产品推荐
相关产品推荐

