.NET 性能最佳做法:避免对HttpRequest/HttpRepsonse正文进行同步读取或写入

ASP.NET Core 中的所有 I/O 都是异步的。服务器会实现 Stream 接口,该接口同时具有同步和异步重载。应首选异步重载以避免阻止线程池线程。阻止线程可能会导致线程池资源不足。

请勿这样做:下面的示例使用 。它会阻止当前线程等待结果。下面是异步中同步的示例。.

public class GoodStreamReaderController : Controller{    [HttpGet("/contoso")]    public async Task<ActionResult<ContosoData>> Get()    {        var json = await new StreamReader(Request.Body).ReadToEndAsync();
        return JsonSerializer.Deserialize<ContosoData>(json);    }}

在上面的代码中,Get 将整个 HTTP 请求正文同步读取到内存中。如果客户端上传速度缓慢,则应用会进行异步中同步。应用进行异步中同步是因为 Kestrel 不支持同步读取。

请这样做:下面的示例使用 ,在读取时不会阻止线程。

​​
public class GoodStreamReaderController : Controller{    [HttpGet("/contoso")]    public async Task<ActionResult<ContosoData>> Get()    {        var json = await new StreamReader(Request.Body).ReadToEndAsync();
        return JsonSerializer.Deserialize<ContosoData>(json);    }}

上面的代码会将整个 HTTP 请求正文同步读取到内存中。

如果请求较大,则将整个 HTTP 请求正文读取到内存中可能会导致内存不足 (OOM) 状况。OOM 可能会导致拒绝服务。 有关详细信息,请参阅本文档中的避免将大型请求正文或响应正文读取到内存中。

请这样做:下面的示例使用非缓冲请求正文完全异步进行:

public class GoodStreamReaderController : Controller{    [HttpGet("/contoso")]    public async Task<ActionResult<ContosoData>> Get()    {        return await JsonSerializer.DeserializeAsync<ContosoData>(Request.Body);    }}

上面的代码将请求正文异步反序列化为 C# 对象。