.Net 7 httpclient, httpresponsemessage.content.readassTreamasync - SpeicherproblemeC#

Ein Treffpunkt für C#-Programmierer
Anonymous
 .Net 7 httpclient, httpresponsemessage.content.readassTreamasync - Speicherprobleme

Post by Anonymous »

Die Umgebung ist ein .NET (.NET7.0) -Dienst, der in einem Linux-basierten Docker-Container ausgeführt wird. Es muss mit einer externen REST-API kommunizieren, eine großzügige Reißverschlussdatei herunterladen, sie entpacken und in einen Azure-Blob-Speicher hochladen. services.AddHttpClient()
.ConfigurePrimaryHttpMessageHandler(createHttpMessageHandler);
< /code>
in einer Fabrik, die vom Verbraucher mit dem folgenden Handler erstellt wird: < /p>
services.AddMyApiClientFactory(() =>
{
var handler = new SocketsHttpHandler()
{
SslOptions =
{
RemoteCertificateValidationCallback = (_, _, _, _) => true
},
PooledConnectionLifetime = TimeSpan.FromMinutes(1),
PooledConnectionIdleTimeout = TimeSpan.FromSeconds(30),
MaxConnectionsPerServer = 3
};
return handler;
});
< /code>
Zertifikatsvalidierung wird aus Gründen umgangen, und die Parameter der gepoolten Verbindungen werden von ChatGPT vorgeschlagen (da ich ahnungslos bin). Mechanismus: < /p>
public async Task GetFilesAsStreamAsync(GetFilesRequest request, CancellationToken cancellationToken = default)
{
string query = String.Empty
.AddParameter(request, x => x.Path)
.AddParameter(request, x => x.FileList); // doesn't matter - just makes a correct query

var requestMessage = new HttpRequestMessage(HttpMethod.Get, "download?" + query);
requestMessage.Headers.ConnectionClose = true;
HttpResponseMessage response = await _httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
response.EnsureSuccessStatusCode();
var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
return new HttpContentStreamWrapper(stream, response);
}
< /code>
Der httpContentStreamWrapper ist da, damit der Verbraucher die Antwort zusammen mit dem von ihm erhälten Stream entsorgen kann. Es hat diese Entsorgung der Logik, sonst ist es nur eine Fassade für Stream: < /p>
protected override void Dispose(bool disposing)
{
if (disposing)
{
_innerStream.Dispose();
_response.Dispose();
}
base.Dispose(disposing);
}

public override async ValueTask DisposeAsync()
{
await _innerStream.DisposeAsync();
_response.Dispose();
await base.DisposeAsync();
}
< /code>
Schließlich macht der Verbraucher Folgendes, wenn es an der Zeit ist, die Datei herunterzuladen und Dinge zu tun (alle Protokollierung und Telemetrie werden abgezogen, hoffentlich alle Logikreste): < /p>
using (var zippedDataStream = await myApiClient.GetFilesAsStreamAsync(request))
{
using (var zip = new ZipArchive(zippedDataStream))
{
var zipEntry = zip.Entries.FirstOrDefault();
using (var entryStream = zip.Entries.First().Open())
{

var storageClient = _blobContainerClient.GetBlobClient(pathInStorage(baseUrl, fileName));
await storageClient.UploadAsync(entryStream, overwrite: true);
}
}
}
< /code>
Der obige Code befindet sich in einer asynchronisierten Methode, die dreimal parallel genannt wird, und alle Aufgaben werden erwartet (es gibt auch Dinge, die passieren, wenn einst eine bestimmte Datei heruntergeladen wurde, aber es scheint Content.ReadassTreamasync (), später vom Anrufer entsorgt werden - sowohl der Stream als auch die Antwort. Meine Hauptfrage lautet: Gibt es grundlegende Memory -Handling -Fehler in diesem Code? Ursprünglich habe ich den Strom nicht explizit entschieden, und angeblich, obwohl er schließlich durch umfangsbasierte Logik entsorgt wurde, war das zugehörige Antwortobjekt nicht. Außerdem wurde der HTTPClient mit einem anderen (älteren) Handler konfiguriert, was laut ChatGPT wiederum nicht gut war. Wir haben also relativ schnell das Gedächtnis mehr ausgehen, um zu tauschen, ebenfalls aus dem Gedächtnis zu gehen und schließlich zu Ärger zu führen. Jetzt scheint es besser zu sein, ich verstehe jedoch nicht ein paar Dinge. Wenn ich nach dem Download jeder Datei explizit GC aufgerufen habe, ist es tatsächlich ein bisschen in Ordnung: Nach dem ersten Download steigt das Speicher, aber dann bleibt es nur dort, anscheinend werden einige Puffer erstellt und werden von den Interna des HTTPCLIENT wiederverwendet -? - Keine Ahnung ... aber ich denke, GC ist explizit nicht koscher, und wenn ich es nicht tue, scheint das Gedächtnis bis zum Containergrenze zu klettern, und bleibt dann dort, also schätze ich, dass GC genug tut, um den Druck zu lindern, aber nicht mehr als das. Ich könnte damit leben. Ich bin zu den 2 Instanzen des Containers zurückgekommen, wobei jeder unter 10% Speicher verwendet wurde (yay!), Aber jeweils auch große Stücke der Swap -Datei des Hosts, die ich nicht bekomme, um ehrlich zu sein. < /P>
Kann mir jemand Einblicke geben? Mache ich etwas grundlegend falsch mit der Entsorgung der Objekte? Gibt es eine Möglichkeit, den Download so umzuschreiben, dass es nicht so viel Speicher benötigt? Vielleicht ist das Problem mit der Entpackung des Streams? Sollte ich die Datei auf die Festplatte herunterladen und die ZIP -Bibliothek von dort aus funktionieren lassen? Jede Eingabe wird geschätzt!

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post