C# 大型HTTPResponseMessage导致.NET核心服务器进程内存不足

C# 大型HTTPResponseMessage导致.NET核心服务器进程内存不足,c#,.net,.net-core,C#,.net,.net Core,我有一个C#.NET 2.2 web服务器进程,它公开了一个API。当请求传入时,服务器需要向数据库API发出自己的HTTP请求。根据查询的不同,来自数据库的响应可能非常大,在某些情况下,响应的大小足以导致.NET进程崩溃,日志中出现(超出内存配额) 发送请求的代码如下所示: string endpoint_url = "<database service url>"; var request_body = new StringContent(query, Encoding.UTF8

我有一个C#.NET 2.2 web服务器进程,它公开了一个API。当请求传入时,服务器需要向数据库API发出自己的HTTP请求。根据查询的不同,来自数据库的响应可能非常大,在某些情况下,响应的大小足以导致.NET进程崩溃,日志中出现
(超出内存配额)

发送请求的代码如下所示:

string endpoint_url = "<database service url>";
var request_body = new StringContent(query, Encoding.UTF8, "<content type>");
request_body.Headers.ContentType.CharSet = "";
try {
    var request_task = Http.client.PostAsync(endpoint_url, request_body);
    if (await Task.WhenAny(request_task, Task.Delay(timeoutSeconds*1000)) == request_task) {
        request_task.Result.EnsureSuccessStatusCode();
        var response = await request_task.Result.Content.ReadAsStringAsync();
        JObject json_result = JObject.Parse(response);
        if (json_result["errors"] is null) {
            return json_result;
        } else {
            // return error
        }
    } else {
        // return timeout error
    }
} catch(Exception e) {
    // return error
}
字符串端点_url=”“;
var request_body=newstringcontent(query,Encoding.UTF8,“”);
请求_body.Headers.ContentType.CharSet=“”;
试一试{
var request\u task=Http.client.PostAsync(端点\u url,请求\u正文);
if(wait Task.WhenAny(request\u Task,Task.Delay(timeoutSeconds*1000))==request\u Task){
request_task.Result.EnsureSuccessStatusCode();
var response=wait request_task.Result.Content.ReadAsStringAsync();
JObject json_result=JObject.Parse(响应);
如果(json_结果[“错误”]为空){
返回json_结果;
}否则{
//返回错误
}
}否则{
//返回超时错误
}
}捕获(例外e){
//返回错误
}
我的问题是,当查询返回这样一个大的响应时,防止web服务崩溃的最佳方法是什么?建议我不应该将响应主体大量加载到字符串中,但实际上并没有建议其他方法

我希望优雅地失败并向客户端返回一个错误,而不是导致.NET服务中断,因此对响应大小设置某种限制是可行的。不幸的是,有问题的数据库服务没有返回
内容长度
头,所以我不能检查它


我的web服务器目前有512MB的可用内存,我知道这并不多,但我担心,无论我有多少可用内存,对于大型响应都可能发生此错误。我主要关心的是保证我的.NET服务不会崩溃,不管数据库服务的响应大小如何。

您可以使用的最简单的方法是根据返回的行数做出决定

如果您使用的是
ExecuteReader
,则它不会返回受影响的行,但您可以通过简单地返回两个结果集来克服此限制。第一个结果集将有一行和一列,这将告诉您行数,并基于此您可以决定是否调用
NextResult
,并处理请求的数据

如果您使用的是存储过程,则可以使用out参数来指示检索到的行数。使用
@@ROWCOUNT
变量或
ROWCOUNT\u BIG()
函数。同样,您可以根据这些数据进行分支

这些解决方案的优点是,如果记录超出可用空间,您不必阅读任何记录


这些解决方案的缺点是确定阈值可能很困难,因为它可能取决于查询本身、查询的一个(或多个)参数、表大小,等等。

当然,您不应该创建一个可能大于堆大小的无界字符串,但它比建议更复杂。正如其他人所指出的,整个系统需要协同工作,才能在有限的内存占用下返回大的结果

对于您的直接问题(如果响应不适合内存,如何发回错误),最简单的答案是创建一个有限的“最大”大小的缓冲区,并从响应中只读取那么多数据。如果它不适合您的缓冲区,那么它太大,您可以返回一个错误

但总的来说,这是一个糟糕的设计,因为“max”不可能静态派生——它取决于服务器负载

更好的答案是避免在将整个结果发送到客户端之前对其进行缓冲,而是将结果流式传输到客户端—在充满数据的缓冲区中读取,然后将该缓冲区或该缓冲区的某种处理形式写入客户端。但这需要后端API、您的服务以及可能的客户端之间的协同作用


如果您的服务必须解析一个完整的对象——正如您用Json.parse所展示的那样——那么您可能需要重新考虑您的总体设计。

如果
Http.client
是一个HttpClient,您可以在中止操作并引发异常之前限制它将读取的最大数据量
MaxResponseContentBufferSize
属性。默认情况下,它被设置为2Gb,这就解释了为什么如果服务器只有512Mb的RAM,它就会消失,所以你可以将它设置为10/20Mb,如果它溢出,你可以处理异常。

对不起,我不明白。如果要限制数据库响应大小,那么为什么要共享与问题无关的代码。我们在谈论什么样的数据库?是否涉及分页?当数据库可以/不应该返回相当大的数据量时,需要采取什么行动。我不是问如何限制数据库响应大小(这对我来说不是一个选项),而是问一般来说如何优雅地处理来自HTTP调用的大响应大小。分页是解决我的问题的一个很好的建议,但它仍然不能保证我的.NET服务器不会耗尽内存(例如,理论上一个数据点可能超过内存限制)。如果可能的话,我希望在不假设HTTP请求另一端有什么的情况下处理这个问题。我希望抛出一个异常(或以某种形式引发一个错误),以便捕获它并将错误代码发送回我的服务的客户端。我不希望每当来自数据库的HTTP响应太大时,我的web服务器崩溃并重新启动