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

Azure App Service上.NET 7 Web API连续请求时性能下降排查

问题描述

部署在Azure App Service中的.NET 7 Web API,在连续发送非并发请求时性能逐渐下降:依次发送25个请求,响应时间从不足1秒开始,每次增加0.1-0.2秒,最终超过6秒。
该API使用GroupDocs.Conversion实现Word文档转PDF功能,核心代码如下:

Program.cs

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

app.UseSwagger();
app.UseSwaggerUI();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

PdfConverterController

[HttpPost]
[Route("convert/word")]
public IActionResult ConvertWord(IFormFile fileToConvert, [FromQuery] string? password)
{
   if (fileToConvert == null || fileToConvert.Length == 0)
       return BadRequest("No file uploaded.");

   try
   {
       var outputPath = Path.Combine(Path.GetTempPath(), fileToConvert.FileName + ".pdf");

       var decodedPassword = string.Empty;
       if (!string.IsNullOrEmpty(password))
           decodedPassword = Encoding.UTF8.GetString(Convert.FromBase64String(password));

       var loadOptions = new WordProcessingLoadOptions
       {
           Password = decodedPassword,
           Format = (WordProcessingFileType)FileType.FromExtension(Path.GetExtension(fileToConvert.FileName)),
       };

       using (Converter converter = new Converter(fileToConvert.OpenReadStream, () => loadOptions))
       {
           var convertOptions = new PdfConvertOptions();
           converter.Convert(outputPath, convertOptions);
       }

       var ms = new MemoryStream();

       using (var fs = new FileStream(outputPath, FileMode.Open))
           fs.CopyTo(ms);

       System.IO.File.Delete(outputPath);

       return File(ms.ToArray(), MediaTypeNames.Application.Pdf);

   }
   catch (Exception ex)
   {
       return StatusCode(StatusCodes.Status500InternalServerError, ex.Message);
   }
}

已尝试的排查动作:

  • 使用using声明或单实例HttpClient调用API,无明显改善
  • 本地控制台程序中GroupDocs.Conversion运行正常
  • App Service为PremiumV3规格,排除资源瓶颈
  • 优化资源释放逻辑并手动触发GC,均无效
  • 通过发布管道部署时问题更严重
排查建议
  • 检查GroupDocs.Conversion非托管资源泄漏:虽然代码用using包裹了Converter,但GroupDocs内部可能存在未正确释放的非托管资源(如字体缓存、Office组件句柄)。尝试升级到最新稳定版本,修复已知泄漏问题;同时排查是否有静态初始化的GroupDocs对象未被清理。
  • 替换临时文件为内存流输出:当前代码将转换结果写入临时磁盘再读取,可能存在Azure临时磁盘IO波动或文件锁定问题。改用GroupDocs支持的直接输出到MemoryStream的重载(如converter.Convert(ms, convertOptions)),跳过磁盘IO步骤,验证性能是否稳定。
  • 启用Azure App Service诊断追踪:在Azure门户开启应用日志、服务器日志和Application Insights,拆分每个请求的耗时(如Converter初始化、转换、文件读写各阶段),同时监控内存、文件句柄的变化趋势,确认是否存在内存持续增长或句柄泄漏。
  • 优化临时文件命名逻辑:当前用fileToConvert.FileName + ".pdf"作为临时文件名,重复文件名可能导致文件锁定或删除失败。改用Guid.NewGuid().ToString() + ".pdf"生成唯一文件名;删除文件时增加重试逻辑(捕获IOException后延迟重试),确保临时文件彻底清理。
  • 排查发布管道部署的特殊配置:发布管道部署后问题更严重,需检查部署过程是否修改了App Service权限(如临时目录权限、字体文件访问权限),或部署包是否缺失GroupDocs依赖的字体/组件。对比手动部署和管道部署的文件目录、权限差异。
  • 分析非托管内存占用:使用Azure内存快照工具或第三方性能分析工具(如dotTrace),抓取请求前后的内存快照,重点排查非托管内存增长情况——若非托管内存持续上涨,说明GroupDocs存在未释放的非托管资源,需调整组件使用方式或联系官方支持。
  • 测试GroupDocs对象复用:非并发场景下,尝试复用Converter或相关配置对象(需确认线程安全性),避免每次请求都初始化全新实例,降低重复初始化开销。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 05:03:32