Aspnetcore: BadHttpRequestException: Zeitüberschreitung beim Lesen des Anfragetexts, da Daten zu langsam ankommen

Erstellt am 16. Okt. 2018  ·  129Kommentare  ·  Quelle: dotnet/aspnetcore

Wir haben eine asp.net Core 2.1-App, die auf .net 4.7 im Azure-Web-App-Dienst ausgeführt wird.

Kürzlich haben wir angefangen, VIELE des folgenden Fehlers zu bekommen:

Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Zeitüberschreitung beim Lesen des Anforderungstexts, da Daten zu langsam ankommen. Siehe MinRequestBodyDataRate.

Unsere App ist ziemlich konstant stark belastet (meistens von eingehenden Webhooks). Während großer Spitzen neigen wir dazu, mehr davon zu sehen.

Wir liefen viele Monate lang auf asp.net core 2.0 / .net 4.6.1 und wir haben diesen Fehler noch nie zuvor gesehen.
Es scheint nach unserem jüngsten Upgrade auf asp.net core 2.1 / .net 4.7 begonnen zu haben.

Wir möchten diesem Thema gerne auf den Grund gehen, sind uns aber noch nicht einmal sicher, wo wir anfangen sollen.
Irgendwelche Gedanken?

affected-medium area-servers enhancement servers-kestrel severity-nice-to-have

Hilfreichster Kommentar

Schön zu hören, dass einige Lösungen finden.
Persönlich mag ich es nicht, Workarounds anzuwenden, die ich nicht wirklich verstehe, ohne das Problem zuerst zu verstehen.

Soweit ich weiß, ist der Fehler "Lesen des Anforderungstexts aufgrund zu langsam ankommender Daten" unter hoher Last nicht die eigentliche Ursache. Es ist eine Folge der Belastung des Systems.
Was ich immer noch nicht herausgefunden habe, ist, WARUM das System gestresst ist, da die Metriken gesund aussehen (meistens CPU, Speicher und Thread-Anzahl).
Ich vermute immer noch, dass dies dadurch verursacht werden könnte, dass der ThreadPool-Algorithmus Threads nicht schnell genug erstellt, wenn eine Spitze von Anfragen auftritt, aber soweit ich weiß, können wir diesen Algorithmus nicht optimieren.

Es wäre fantastisch, wenn das Kernteam von asp.net einen Spike-Stresstest für eine einfache Web-App erstellen könnte, um zu bestätigen, ob tatsächlich ein Engpass auf Thread-Pool-Ebene oder anderswo vorliegt.

Alle 129 Kommentare

@sebastienros hast du schon ein FAQ-Wiki erstellt?

Ich frage mich, ob es daran liegt, dass der Threadpool verhungert... Ist Ihre Anwendung vollständig asynchron? Haben Sie andere Protokolle von Kestrel? Können Sie einen Dump oder ein Profil erfassen? Ist die Anwendung CPU-gebunden oder IO-gebunden?

Es ist nur ein paar Mal zuvor passiert, normalerweise passiert es für mehrere aufeinanderfolgende Anfragen in kurzer Zeit.
Ich überwache CPU, Arbeitsspeicher, Threadanzahl und eine Reihe anderer Metriken in Azure Monitor und alles sieht fehlerfrei aus.
Selbst wenn kein offensichtlicher Engpass vorliegt und die App sehr reaktionsschnell ist, kann es dennoch gelegentlich vorkommen.
Ja, es ist vollständig asynchron.

Haben Sie andere Protokolle von Kestrel?

Keine weiteren Warnungen/Fehler. Wahrscheinlich Info-Level, aber ich beharre nicht darauf, obwohl es wahrscheinlich eine Möglichkeit gibt, diese zu bekommen.

Können Sie einen Dump oder ein Profil erfassen?

Ich sollte in der Lage sein, eine Müllhalde vom azurblauen Portal zu erfassen, wenn Sie glauben, dass dies hilft?
Sie sind sich nicht sicher, was ein Profil ist?

Haben Sie eine Idee, wie Sie diesem Problem auf den Grund gehen können?

Wir sind uns nicht sicher, ob die Fehler durch legitime Anfragen verursacht werden.
Ist es möglich, die beim Generieren dieser Nachricht angeforderte URL herauszufinden?

Wir erhalten ein paar Hunderte davon pro Tag, obwohl alle Metriken gesund aussehen.

Irgendeine bestimmte Protokollquelle, die ich vielleicht in azure/asp.net core aktivieren kann?

Der Stack-Trace gibt uns nicht viel.

Exception: Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Reading the request body timed out due to data arriving too slowly. See MinRequestBodyDataRate.
   at Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException.Throw(RequestRejectionReason reason)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1MessageBody.d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.IO.Pipelines.PipeCompletion.ThrowLatchedException()
   at System.IO.Pipelines.Pipe.GetReadResult(ReadResult& result)
   at System.IO.Pipelines.Pipe.TryRead(ReadResult& result)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1MessageBody.OnConsumeAsync()

Es gab nur einen Protokolleintrag mit einem etwas besseren Call-Stack, einschließlich einiger unseres Aufrufcodes.
Der Stack zeigt auf unsere Webhook-Aktion.
Um zu überprüfen, ob der Webhook gültig ist, müssen wir den Anfragetext hashen und verwenden den folgenden Code.
Stimmt etwas mit diesem Code nicht?

[HttpPost]
        public async Task<OkResult> Webhook()
        {
            var memoryStream = new MemoryStream();
            await Request.Body.CopyToAsync(memoryStream);
            bool isValidRequest = await AuthorizationService.IsAuthenticWebhook(Request.Headers, memoryStream, _shopifyLibOptions.SecretKey);
            if (!isValidRequest)
            {
                throw new UnauthorizedAccessException("This request is not an authentic webhook request.");
            }
            memoryStream.Position = 0;
            string body = new StreamReader(memoryStream).ReadToEnd();

        //omitted code here unrelated to asp.net

            return Ok();
        }

Die Aufrufliste zeigt auf die Leitung
bool isValidRequest = await AuthorizationService.IsAuthenticWebhook(Request.Headers, memoryStream, _shopifyLibOptions.SecretKey);
Obwohl die Zeilennummern in der Anrufliste meiner Erfahrung nach manchmal etwas abweichend sind und ich frage mich, ob das wirkliche Problem die Zeile vor await Request.Body.CopyToAsync(memoryStream);

Dann ist der Stapel unten

Exception: Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Reading the request body timed out due to data arriving too slowly. See MinRequestBodyDataRate.
   at Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException.Throw(RequestRejectionReason reason)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1MessageBody.d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.IO.Pipelines.PipeCompletion.ThrowLatchedException()
   at System.IO.Pipelines.Pipe.GetReadResult(ReadResult& result)
   at System.IO.Pipelines.Pipe.GetReadAsyncResult()
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.MessageBody.d__22.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpRequestStream.d__23.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at LINE FROM OUR CODE

.ReadToEnd(); ist ein rotes Flag, das die Synchronisierungs-E/A blockiert. Dieser Datenratenfehler wurde oft in Anwendungen gemeldet, die Probleme mit zu vielen blockierten Threadpool-Threads haben. In diesem Fall gibt es einen einfachen ReadToEndAsync() Ersatz. Haben Sie an anderer Stelle nach Problemen mit Thread-Hunger gesucht?

Aber der Strom hier ist ein Speicherstrom. Aus diesem Grund wird die Synchronisierungsversion verwendet. Keine E/A richtig?

Die Anzahl der Threads ist gering und die Metriken sind fehlerfrei.
Dieses Problem scheint ziemlich sporadisch zu sein, aber wir erhalten diese Fehler in Stapeln von 5 oder 10 ...
Könnte es daran liegen, dass der GC eine Pause verursacht hat?

Ach, du hast mich. Ich habe nicht genau genug gelesen.

Wie groß sind die Beiträge?

Sie sind ziemlich kleine Json-Nutzlasten. Bestellung, Kunde, Produkt, so etwas...

Welche Größe ist ziemlich klein?

Normalerweise unter 10.000 Zeichen, obwohl es einige Ausreißer über 100.000 Zeichen gibt.
Trotzdem bekommen wir viele Anfragen. Ungefähr 500.000 pro Tag.

Gibt es weitere Protokolle, die aktiviert werden können, um diesem Problem auf den Grund zu gehen?
Dies verschmutzt unsere Protokolldateien wirklich und wir sind uns immer noch nicht sicher, warum es überhaupt auftritt.

Wir scheinen das Problem losgeworden zu sein.
Wir sind uns nicht ganz sicher, aber höchstwahrscheinlich wurde dies durch Code verursacht, der von einem Hintergrundthread auf das Anforderungsobjekt zugreift.

Wir sind uns nicht ganz sicher, aber höchstwahrscheinlich wurde dies durch Code verursacht, der von einem Hintergrundthread auf das Anforderungsobjekt zugreift.

Verwenden des IHttpContextAccessor ? Wie sah dieser Code aus?

Tut mir leid, dass ich wieder öffnen musste.
Ich dachte es sei nach unserem Einsatz am Sonntagabend behoben, aber das Fehlen von Fehlern lag einfach an der sehr geringen Aktivität zu dieser Wochenzeit.
Als der Montag einsetzte, begann dieser Fehler erneut zu fluten.

David, diese Bereitstellung enthielt einen Fix im Zusammenhang mit dem IHttpContextAccessor, den Sie mir hier lösen helfen. Ich dachte fälschlicherweise, dass dies auch dieses Problem behoben haben könnte.

Die Aufrufliste variiert geringfügig, wenn der Fehler im Protokoll erfasst wird. Normalerweise nur asp.net-Frames, aber manchmal passiert es auch in diesem neuen Code, der demjenigen sehr ähnlich ist, den ich oben zur Behandlung von Webhooks gepostet habe:

Zur Verdeutlichung etwas vereinfacht:

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Http.Internal;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Middlewares
{
    public class RequestLogScopeMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<RequestLogScopeMiddleware> _logger;

        public RequestLogScopeMiddleware(RequestDelegate next, ILogger<RequestLogScopeMiddleware> logger)
        {
            _next = next;
            _logger = logger;
        }

        public async Task Invoke(HttpContext context)
        {
            using (_logger.BeginScope(await GetHttpInfo(context)))
            {
                await _next(context);
            }
        }

        private async Task<HttpInfo> GetHttpInfo(HttpContext ctx)
        {
            try
            {
                var req = ctx.Request;
                req.EnableRewind();
                string body = await new StreamReader(req.Body).ReadToEndAsync();
                req.Body.Position = 0;
                try
                {
                    body = JsonConvert.SerializeObject(JsonConvert.DeserializeObject(body), Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
                }
                catch
                {
                    //body may not be json
                }
                return new HttpInfo(req.Method, req.GetDisplayUrl(), body, string.Join(Environment.NewLine, req.Headers.Select(kv => $"{kv.Key}: {kv.Value}")));
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "Failed to extract http info from request");
                return null;
            }
        }
    }
}

Protokollierter Fehler:

Failed to extract http info from request

    HttpInfo: 

    Exception: Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Reading the request body timed out due to data arriving too slowly. See MinRequestBodyDataRate.
   at Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException.Throw(RequestRejectionReason reason)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1MessageBody.d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.IO.Pipelines.PipeCompletion.ThrowLatchedException()
   at System.IO.Pipelines.Pipe.GetReadResult(ReadResult& result)
   at System.IO.Pipelines.Pipe.TryRead(ReadResult& result)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1MessageBody.OnConsumeAsync()

Ich bin mir ziemlich sicher, dass es mit await new StreamReader(req.Body).ReadToEndAsync();

Hier ist ein weiterer Stack, der deutlich zeigt, dass das Lesen des Streams das Problem ist:

Error from Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware:
    An unhandled exception has occurred while executing the request.

    HttpInfo: 

    Exception: Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Reading the request body timed out due to data arriving too slowly. See MinRequestBodyDataRate.
   at Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException.Throw(RequestRejectionReason reason)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1MessageBody.d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.IO.Pipelines.PipeCompletion.ThrowLatchedException()
   at System.IO.Pipelines.Pipe.GetReadResult(ReadResult& result)
   at System.IO.Pipelines.Pipe.ReadAsync(CancellationToken token)
   at System.IO.Pipelines.Pipe.DefaultPipeReader.ReadAsync(CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.MessageBody.d__21.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpRequestStream.d__21.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.WebUtilities.FileBufferingReadStream.d__35.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.IO.StreamReader.d__97.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.IO.StreamReader.d__62.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Middlewares.RequestLogScopeMiddleware.d__5.MoveNext()

@halter73 irgendwelche ideen?

PS: Verwenden Sie diese Middleware in der Produktion? Es sieht furchtbar ineffizient aus.

Ja, wir verwenden dies in Prod.
Wir haben keine Leistungsprobleme erlitten, aber ich höre zu, wenn Sie Ideen haben, wie man es performanter machen kann.
Ein Protokoll des Hauptteils ist sehr nützlich, um zu untersuchen, warum eine bestimmte Anfrage Fehler aufwies...

Ein Protokoll des Hauptteils ist sehr nützlich, um zu untersuchen, warum eine bestimmte Anfrage Fehler aufwies...

Haben Sie eine strukturierte Protokollierung? Sie müssen nicht so viele Informationen an den Bereich anhängen, Sie müssen nur genügend Informationen an jede Anfrage anhängen, damit Sie alle korrelierten Protokolle finden können. Sie würden den Körper in Blöcken protokollieren und sich dann die gesamte Nutzlast ansehen, indem Sie alle Protokolle nach einer bestimmten Anforderungs-ID durchsuchen.

Wir haben keine Leistungsprobleme erlitten, aber ich höre zu, wenn Sie Ideen haben, wie man es performanter machen kann.

Es ist ein DoS, das darauf wartet, zu passieren. Ich kann einfach eine große Nutzlast an Ihre Anwendung und Boom senden, wenn der Speicher nicht reicht. (Ein Beispiel finden Sie unter https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/master/Scenarios/Controllers/BigJsonInputController.cs). Es wird auch eines der ASP.NET Core-No-Nos sein (siehe https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/master/AspNetCoreGuidance.md#avoid-reading-the-entire-request-body-or -Antwort-Körper-in-Gedächtnis)

Ein Protokoll des Hauptteils ist sehr nützlich, um zu untersuchen, warum eine bestimmte Anfrage Fehler aufwies...

Ja, ich habe es jetzt oft genug gesehen, dass ich denke, wir sollten sofort etwas effizienteres bauen https://github.com/aspnet/AspNetCore/issues/3700

Haben Sie eine strukturierte Protokollierung? Sie müssen nicht so viele Informationen an den Bereich anhängen, Sie müssen nur genügend Informationen an jede Anfrage anhängen, damit Sie alle korrelierten Protokolle finden können. Sie würden den Körper in Blöcken protokollieren und sich dann die gesamte Nutzlast ansehen, indem Sie alle Protokolle nach einer bestimmten Anforderungs-ID durchsuchen.

Ich denke, man könnte sagen, es ist strukturiert, ja.
Die Leiche in Stücken protokollieren? Ich bin mir nicht sicher, was du meinst?
Bedeutet das, dass ich den Body aus mehreren Log-Einträgen rekonstruieren muss?
Es wäre sicherlich viel einfacher, einen einzigen Eintrag mit dem vollständigen Körper zu haben.

Außerdem handelt es sich bei der überwiegenden Mehrheit der Anfragen, die wir erhalten, um Webhooks, die verschiedene Formen annehmen können (aber immer im Json-Format), und wir müssen ihre Authentizität durch Hashing des Hauptteils überprüfen.
(siehe die Methode Webhook() , die ich oben gepostet habe)
Daher können wir [FromBody] da die Nutzlast von Anfrage zu Anfrage sehr unterschiedlich sein kann.

Es ist ein DoS, das darauf wartet, zu passieren. Ich kann einfach eine große Nutzlast an Ihre Anwendung und Boom senden, wenn der Speicher nicht reicht.

Ich sehe, wie das ein Problem sein könnte.
Soll ich etwas wie BigContentManualGood() aus Ihrem verlinkten Beispiel verwenden?
Soweit ich das beurteilen kann, lädt es immer noch das gesamte Json im Speicher in ein JObject, sodass ich nicht sehe, wie sich das unterscheidet.
Ich denke, es muss auch im asp.net-Kern eine vernünftige Standardeinstellung für die maximale Anforderungsgröße geben?

Um auf das ursprüngliche Problem zurückzukommen, wollte ich nur darauf hinweisen, dass das Muster, das wir sehen, darin besteht, dass diese Fehler im Batch auftreten.
Es wird eine Weile (5 Minuten oder sogar bis zu ein paar Stunden) keine Probleme geben, und dann erhalten wir plötzlich viele Male diesen Fehler in schneller Folge (sagen wir zwischen 3 und 10+).
Das kann eine nützliche Information sein.
Das brachte mich dazu, zu denken, dass es durch eine längere als übliche GC-Pause verursacht werden könnte ... Nur eine Idee ...

Was ist der Unterschied zwischen den folgenden beiden?

await new StreamReader(Request.Body).ReadToEndAsync();
await new HttpRequestStreamReader(Request.Body, Encoding.UTF8).ReadToEndAsync();

Wir haben heute erneut eingesetzt, um zu versuchen, dies zu beheben.
Wir sammeln die Leiche beim Loggen nicht mehr, basierend auf den guten Kommentaren von @davidfowl zu Perf und Sicherheit.
Wir haben alle Verwendungen von HttpAccessor entfernt und uns bleibt das einzige andere Stück Code, das den gesamten Text liest und den Fehler verursacht, in unserer Aktion Webhook() , die jetzt so aussieht:

        [HttpPost]
        public async Task<OkResult> Webhook()
        {
            var body = await new HttpRequestStreamReader(Request.Body, Encoding.UTF8).ReadToEndAsync();
            bool isValidRequest = AuthorizationService.IsAuthenticWebhook(Request.Headers, body, _options.SecretKey);
            if (!isValidRequest)
            {
                throw new UnauthorizedAccessException("This request is not an authentic webhook request.");
            }
            //handle webhook
            return Ok();
        }

Die Verwendung von HttpRequestStreamReader hat das Problem nicht behoben.

Wir wollten auch eine vorübergehende Erschöpfung des Threadpools regeln (obwohl der Threadzähler im Azure-Portal in Ordnung aussieht), da wir dachten, dass der Threadpool einige Zeit brauchen könnte, um Threads hochzufahren, und möglicherweise eine Spitze von Anforderungen zu vorübergehendem Hunger führen könnte.
Aber es sieht so aus, als ob es viele Threads gibt und der Fehler immer noch auftritt...

public override void OnException(ExceptionContext context)
{
    context.ExceptionHandled = true;
    ThreadPool.GetAvailableThreads(out var workerThreads, out var completionPortThreads);
    ThreadPool.GetMaxThreads(out var maxWorkerThreads, out var maxCompletionPortThreads);
    _logger.LogError(context.Exception, $"Error in global mvc exception filter. Available Threads = ({workerThreads:N0}/{maxWorkerThreads:N0}, {completionPortThreads:N0}/{maxCompletionPortThreads:N0}).");
}
Error in global mvc exception filter. Available Threads = (32,757/32,767, 1,000/1,000).

    Exception: Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Reading the request body timed out due to data arriving too slowly. See MinRequestBodyDataRate.
   at Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException.Throw(RequestRejectionReason reason)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1MessageBody.d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.IO.Pipelines.PipeCompletion.ThrowLatchedException()
   at System.IO.Pipelines.Pipe.GetReadResult(ReadResult& result)
   at System.IO.Pipelines.Pipe.TryRead(ReadResult& result)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1MessageBody.OnConsumeAsync()

Uns gehen die Ideen aus.

@clement911 Wie

Wir haben anscheinend das gleiche Problem. ASP.NET Core 2.1-App in einer Azure App Service (Windows)-Bereitstellung mit ähnlichem Verhalten – für ein paar Minuten in Ordnung, dann löst eine Gruppe von Verbindungen alle diese Ausnahme aus, dann wieder in Ordnung.

Alle Anfragen sind POSTs mit einem JSON-Body, der in einer Middleware gelesen und deserialisiert wird.

Hallo @tstuts ,
Es handelt sich um eine Bereitstellung mit 3 bis 5 S2-Instanzen und wir erhalten eine Menge Anfragen, hauptsächlich Webhooks.
Mehrere 100.000 pro Tag.

Ich bin mir nicht sicher, ob es mit diesem Problem zusammenhängt, aber wir haben auch festgestellt, dass wir viel mehr der folgenden Fehler erhalten. Bei weitem nicht so viel, aber früher haben wir es vielleicht einmal am Tag bekommen und jetzt bekommen wir es viel mehr, und ich habe das Gefühl, dass sie ungefähr zur gleichen Zeit wie das ursprüngliche Problem auftreten.

Warning from Microsoft.AspNetCore.Server.Kestrel:
    Heartbeat took longer than "00:00:01" at "11/01/2018 19:48:02 +00:00".

Das bedeutet normalerweise, dass es einen Thread-Pool-Hunger gibt ... Hmm

@davidfowl Ich verstehe nicht, wie dies möglich ist, da ThreadPool.GetAvailableThreads berichtet, dass viele Threads verfügbar sind?
Wir erstellen keine manuellen Threads.
Wir haben ein paar Task.Run() und auch mehrere periodische Hintergrundjobs, die über Observable.Timer , was den ThreadPool unter der Haube verwendet.

Hier ist ein Screenshot aus dem Azure-Portal, der unsere Threadanzahl in den letzten 12 Stunden zeigt.
Ich glaube nicht, dass mehr als 200 Threads übertrieben sind?

image

200 Threads sind viel mehr als Sie Kerne haben. Wenn diese Threads alle aktiv sind, erhalten einige von ihnen möglicherweise nicht genügend CPU-Zeit (ähnlich wie beim Thread-Pool-Aushungern). Wir nehmen einige Änderungen an den Zeitgebern für die Datenrate vor, um diese Szenarien abzumildern. https://github.com/aspnet/KestrelHttpServer/pull/3070. Das heißt, Ihr System ist wahrscheinlich immer noch überfordert.

Wenn dies der Fall ist, denke ich, dass eine Erhöhung der Anzahl der Instanzen das Problem beheben könnte.

Verzeihen Sie hier meine Unwissenheit, aber ich verstehe es nicht.
CPU und Speicher scheinen mir gesund zu sein (siehe Screenshots unten).
Wir führen S2-Instanzen aus, aber das Azure-Portal empfiehlt S1.
Ich habe versucht herauszufinden, wie viele Kerne S2 hat, aber ich kann nur sagen, dass sie 200 ACU (Azure Compute Units) haben, was auch immer das bedeutet.

Ich habe die letzten MinRequestBodyDataRate-Fehler durchlaufen und der Unterschied zwischen den verfügbaren Laufflächen und den maximalen Threads ist immer kleiner als 100, also gibt es weniger als 100 Worker-Threads von einem vermeintlich sinnvollen (?) Standardmaximum von 32.757! 100/32757 ist weniger als 1/3 von 1%.

Ich nehme an, ich könnte die Anzahl der Instanzen erhöhen und vielleicht wird dies das Problem beheben, aber ich denke, ich würde gerne verstehen, was der Engpass ist, da er für mich überhaupt nicht offensichtlich ist. (anstatt Geld auf das Problem zu werfen).

image

image

Verwenden Sie Task.Wait oder Task.Result irgendwo?

Danke @davidfowl das war eine gute Auffrischung.

Verwenden Sie Task.Wait oder Task.Result irgendwo?

Nein.
Ich werde weiterhin nach blockierenden Anrufen suchen, die ich möglicherweise verpasst habe.

Übrigens verwenden wir keinen .net-Core.
Wir verwenden .net 4.7.1, aber ich gehe davon aus, dass der Thread-Pool ähnlich funktioniert.

@clement911 ja, es ist im Grunde das gleiche (Kern ist etwas optimierter)

Der .NET-Threadpool versucht, mehr Threads einzuschleusen, aber mit einer bescheidenen Geschwindigkeit (zB 1 oder 2 pro Sekunde), so dass es kurzfristig keinen Unterschied macht (Sie brauchen viele Minuten, um auf 1000 zu kommen).

Ich frage mich, ob das ein Problem ist. Das klingt nicht nach einer guten Strategie, um mit temporären Spitzen umzugehen.

Danke, ich werde Ben.BlockingDetector überprüfen.

Unsere Webhook-Methode überprüft lediglich, ob sie authentisch ist (CPU-gebunden) und speichert sie in der Datenbank mit ef core (async IO).

Mir ist aufgefallen, dass das Auftreten des MinRequestBodyDataRate-Fehlers wahrscheinlich (aber nicht immer) in Verbindung mit einer Warnung für mehrere andere Webhooks auftritt, die besagen, dass das Speichern in der Datenbank absurd viel Zeit gedauert hat (z. B. über 40 Sekunden und sogar bis zu .). 180 Sekunden!), um die Webhook-Daten zu speichern.
Ich dachte, es könnte die EF-Core-Wiederholungsstrategie sein, die mehrere Wiederholungen durchführt, aber soweit ich das beurteilen kann, reagiert die Datenbank sehr reaktionsschnell und ich habe in EF Core kein Ereignis gefunden, das bei einer Wiederholung protokolliert wird ...
Es scheint ein seltsamer Zufall zu sein, daher gehe ich davon aus, dass es an der gleichen Threaderschöpfung liegt.

Ja, diese Symptome klingen genau nach Hunger. Das Erstellen eines Speicherabbilds, wenn das Problem auftritt, kann bei der Überprüfung helfen. Wenn Sie so viele Threads haben, können Sie sich auch ansehen, was diese Threads tun, und es kann auf den problematischen Bereich hinweisen.

Danke Jungs, ich freue mich sehr über die Hilfe

Welcher HTTP-Statuscode wird an den Client zurückgegeben, wenn eine MinRequestBodyDataRate / BadHttpRequestException auftritt?
Ich würde denken, ein 4xx? (auch wenn es in meinem Fall wahrscheinlich ein internes Problem ist)

Wir erhalten viele dieser Fehler in unseren Protokollen, aber die Azure-Portalmetriken behaupten, dass es kaum HTTP 4xx- oder 5xx-Antworten gibt. Ich verstehe es nicht.

Ich habe einen Speicherabzug vom azurblauen Portal (zweimal) erhalten, in der Hoffnung, dass es etwas Licht ins Dunkel bringen würde, aber im Fenster der parallelen Stapel wird nichts Offensichtliches angezeigt. Es gibt nur sehr wenige Threads (7) mit einem verwalteten Stack und nichts, das außer Program.Main und anderen üblichen verdächtigen Threads (wie der ConsoleLoggerProcessor-Warteschlange) blockiert.
Alle anderen Threads zeigen im Thread-Fenster "Nicht verfügbar" an.

Ich habe einen anderen Weg ausprobiert, um Stapel aus allen Threads zu erhalten, was funktionierte, aber das zeigte dasselbe.

Ich habe auch versucht, lokal zu replizieren, indem ich ein kleines Programm erstellt habe, das gefälschte Webhooks sendet, und ich konnte das Problem nicht reproduzieren, aber zumindest werden alle Threads angezeigt (obwohl asynchrone Aufrufstapel wie üblich kaum lesbar sind).

Aus Verzweiflung habe ich (widerwillig) Application Insights aktiviert, das, wie ich gelesen habe, Metriken sammeln konnte, ohne sie erneut bereitzustellen. Es hat nicht funktioniert. Keine der Metriken zeigt Daten an. Möglicherweise muss ich mir die Build-Time Application Insights-Konfiguration ansehen.

Ich weiß nicht, ob ich den azurblauen Metriken sowieso vertrauen kann. Wie oben erwähnt, meldet Azure Monitor keine http-Fehler...

Das Problem scheint mit der zunehmenden Aktivität, die wir im Laufe der Zeit erhalten, immer schwerwiegender zu werden.
:müde: :verwirrt:

Die untenstehende Metrik aus dem Dump hat meine Aufmerksamkeit erregt, aber der Finalizer-Thread-Stack hat keinen Benutzercode.

image

Übrigens war ich überrascht zu sehen, dass 3 der Threads von der ConsoleLogger MessageProcessor Queue übernommen wurden. Da habe ich mich gefragt, warum es standardmäßig in Prod aktiviert ist ...

Also habe ich damit gekämpft und wollte über meine Ergebnisse berichten.

Wir mussten also schnell handeln und beschlossen, eine exakte Kopie unserer App in einem neuen App Service Plan bereitzustellen.
Anschließend haben wir alle Ihre Webhook-Abonnements so verschoben, dass sie auf die neue Instanz verweisen.
Der ursprüngliche App-Dienst verarbeitet also nur Anfragen von Benutzern und hat ihn sehr stabil gemacht. Keine Fehler.
Der neue App-Dienst verarbeitet Webhooks und TUT NICHTS ANDERES.
Der neue App-Dienst wird den Benutzern nicht einmal angezeigt und die einzigen Anforderungen, die getroffen werden, sind eine einfache Webhook()-Aktion, die einen Hash des Körpers durchführt und den Körper in der Datenbank speichert, indem er einen asynchronen Aufruf über SqlCommand mit einem await cmd.ExecuteNonQueryAsync();
Nun, der MinRequestBodyDataRate-Fehler tritt immer noch auf! (auf der neuen Instanz natürlich).

Da in der neuen Instanz nur sehr wenig Code ausgeführt wird, bin ich zuversichtlich, dass wir nicht versehentlich eine Synchronisierungs-API aufrufen oder Threads blockieren.
In den Protokollen können wir deutlich sehen, dass die Fehler bei einer Zunahme der Anfragen auftreten.
Ich denke, dass das Problem entweder im SQL-Verbindungspool oder im Threadpool liegen kann.

Bei regelmäßigem Benutzerverkehr ist nicht mit einem plötzlichen Anstieg der Anfragen zu rechnen. Es fährt normalerweise langsam hoch und der Thread-Pool scheint darin ziemlich gut zu sein.
Auf der anderen Seite können wir bei Webhooks manchmal extrem plötzliche Spitzen bekommen, weil einige Systeme irgendwo eine Menge Ereignisse erzeugt haben.
Aber der Threadpool erzeugt Threads sehr langsam.

Ich überlege, mit ThreadPool.SetMinThreads und ADO.net 'MinPoolSize' zu experimentieren.
Es kann eine gute Strategie sein, viele Threads und/oder Verbindungen am Laufen zu halten, um Spitzen zu bewältigen.
Wenn jemand Erfahrung in diesem Bereich hat, höre ich zu.
Diese Einstellungen sind einfach einzustellen, aber es gibt sehr wenig Dokumentation darüber, wie es hinter den Kulissen funktioniert, und es fühlt sich sehr kantig an. Wahrscheinlich ein guter Grund, außer wenn Sie auf ein Szenario wie dieses stoßen...

Klingt, als wärst du auf dem richtigen Weg. Eine Alternative, die wir für solche Szenarien in Betracht gezogen haben, ist die Anforderungsdrosselung. Dies würde die Anforderungen an den Threadpool reduzieren.

Sie wissen nicht genau, wie Sie Ihre Drosselung implementieren und wie dies das Problem lösen würde?

Ein Problem besteht darin, dass die meisten Webhook-Anbieter (die Systeme/APIs, die die Anforderungen senden) normalerweise von einem Listener (wie uns) verlangen, dass er ziemlich schnell mit einem HTTP 200 reagiert, oder sie hielten dies für einen Fehler.
In unserem Fall sind es 5 Sekunden.

Es ist an dieser Stelle nur theoretisch, aber ASP.NET 4 hat diese Klasse von Problemen mit einem ähnlichen Ansatz vermieden. Die Verarbeitung Ihrer Anfragen ist nicht extrem teuer, es gibt nur viele von ihnen und sie konkurrieren um Ressourcen wie Threads, CPU-Zeit, Arbeitsspeicher usw. Wenn Sie diese Anfragen in eine Warteschlange stellen und nur wenige gleichzeitig verarbeiten, gibt es keine längere Kämpfe und sie können schnell abgeschlossen werden. Eine naive Version davon wäre eine Middleware, die einen Mechanismus wie SemaphoreSlim verwendet, um die Anzahl gleichzeitig verarbeiteter Anfragen zu begrenzen. Solange alles reibungslos abläuft, gibt es keinen Grund, warum Sie nicht in der Lage sein sollten, große Bursts rechtzeitig zu bewältigen. Du musst nur verhindern, dass dich die Explosion erdrückt.

@clement911 können Sie das Problem auf der Website mit Webhooks reproduzieren, wenn Sie es mit ähnlichem Datenverkehr hämmern?

@Tratcher das ist ein interessanter Vorschlag. Wenn jedoch eine Anforderung auf einen SemaphoreSlim wartet, wird dies für die IO-Threads des ThreadPools angerechnet.
Ich denke, der schwierige Teil wird darin bestehen, den Sweetspot der Anzahl von Threads im Pool zu finden, um maximalen Durchsatz zu erzielen.

@davidfowl hosten beide App-Dienste genau den gleichen Code. Ich habe versucht, das Problem lokal mit einer naiven lokalen Programmanforderung zu reproduzieren, aber das hat nicht funktioniert. Es ist wahrscheinlich möglich, mit einem geeigneten Lasttest-Tool zu reproduzieren, wenn Sie dies wünschen.

SemaphoreSlim hat einen asynchronen Waiter, damit keine Threads blockiert werden.

@davidfowl Ich glaube, ich habe ein sehr ähnliches Problem mit 2.1.5 beim Umgang mit Azure DevOps-Webhooks in einem Testwebdienst. Die Anzahl der Anfragen ist gering (Bursts von 6 nur beim Einreihen von Builds). In meinem Fall reproduziert es vielleicht 5-10% der Bursts. Wieder lese/hashe ich den Körper, um ihn gegen eine Signatur zu validieren.

@clement911 @mmitche Seht ihr auch viel von dieser Ausnahme?

Microsoft.AspNetCore.Connections.ConnectionResetException: Eine bestehende Verbindung wurde vom Remote-Host zwangsweise geschlossen

@tstuts ja.

Ich vermute, dass eine Anfrage, die nicht schnell genug verarbeitet wird, dazu führt, dass das clientseitige Zeitlimit überschritten wird und die Verbindung geschlossen wird, was zu dieser Ausnahme führt. Nur ein weiteres Symptom des gleichen Grundproblems. Klingt das richtig @davidfowl?

Angesichts der sehr spitzen Natur dieses Fehlers habe ich mich auch gefragt, ob Azure möglicherweise einige periodische Aufgaben auf der VM ausführt, die dies verursachen könnten ... so oft...

Ich erinnere mich nicht an diese Ausnahme, aber ich werde meinen Service jetzt ein wenig mehr testen, damit ich Sie wissen lassen kann, was herausfällt.

Wir erleben dies auch mit einer API, die GeoJSON nimmt und in eine Azure SQL-Datenbank schreibt. Die Anfragen kommen in Bursts und es gibt Hunderte von Anfragen gleichzeitig, da wir die Daten in Batches aufteilen, um zu vermeiden, dass die Länge der Anfragen nicht erreicht wird.

Es sieht auch nach Thread-Hunger aus, wir laufen auf einer einzelnen S3-Instanz:
image

Ich werde versuchen, das Problem basierend auf den Informationen in dieser Diskussion zu lösen, und wenn es erfolgreich ist, werde ich meine Ergebnisse hier berichten.

Bisher nicht viel zu berichten. Ich muss meinen vorherigen Kommentar korrigieren: Wir hosten eine Reihe von ASP.NET- und ASP.NET Core-Anwendungen in einem einzigen App Service als virtuelle Anwendungen.

Ich nahm an, dass das Problem in einer bestimmten API liegt, da diese die meiste Last empfängt, aber ich habe keine offensichtlichen blockierenden Aufrufe gefunden. Ich habe Ben.BlockingDetector auf meinem lokalen Computer ausprobiert, aber fast alle gemeldeten Instanzen wurden anscheinend durch Protokollierung verursacht ('ETL' erschien im Stack-Trace).
Ich habe auch PerfView ausprobiert, aber es ist nicht gelungen, die gewünschte Ausgabe davon zu erhalten.

Was die Thread-Anzahl angeht, erreichen wir aufgrund von w3wp + den verschiedenen darauf laufenden Anwendungen durchschnittlich 180 im Leerlauf. Vielleicht gibt es in diesem Bereich also kein großes Problem. Ich werde weiterhin Lasttests durchführen und Diagnosen durchführen, um die Ursache des Problems zu lokalisieren.

Wir sehen die gleichen Fehlermeldungen, konnten sie jedoch mit den Netzwerkdrosselungsprofilen von Chrome reproduzieren. Nachdem wir eine mit 1kb/s (auf/ab) und 5K ms Latenz erstellt hatten, konnten wir die Auswirkungen des Erhaltens von BadHttpRequestExceptions mit "Zeitüberschreitung beim Lesen des Anforderungstexts aufgrund zu langsam ankommender Daten. Siehe MinRequestBodyDataRate" sehen.

In unserem Produktionsfall war das Problem ein mobiler Client mit sehr schwacher Internetverbindung, der Daten herunterlädt.

@davidfowl Ist eine harte Ausnahme der beste Weg, damit umzugehen? Benötigen wir nur Middleware, um langsame Clients zu verarbeiten und die Ausnahme zu schlucken?

Wie in unserem Fall werden die App-Dienste recycelt (angenommen aufgrund von so etwas wie schnellem Ausfallschutz). Ich erinnere mich nicht an so etwas in IIS 7, auf dem ASP.NET 4.5 ausgeführt wird. Obwohl IIS den App-Pool deaktiviert und die Anwendung vollständig offline genommen hätte ...

Bei mir auch das gleiche Problem.
Hier ist mein Code, der Foto-Uploads von mehreren über GPRS verbundenen Kameras bereitstellt (Ein- und Ausschalten, um Energie zu sparen):

        [HttpPost("photo/post")]
        public async Task PostLegacyAsync()
        {
            var content = new MultipartFormDataContent();
            foreach (var pair in this.Request.Form)
            {
                content.Add(new StringContent(pair.Value), pair.Key);
            }
            foreach (IFormFile ff in this.Request.Form.Files)
            {
                byte[] data;
                using (var br = new BinaryReader(ff.OpenReadStream()))
                {
                    data = br.ReadBytes((int)ff.OpenReadStream().Length);
                }
                content.Add(new ByteArrayContent(data), ff.Name, ff.FileName);
            }

            // ...

            this.Ok();
        }

Dieser Dienst gilt für unser System als veraltet, es müssen jedoch noch mehrere Geräte aktualisiert werden. Verbesserungsvorschläge sind willkommen.

Als Randnotiz könnte ich manchmal einen zufälligen Fehler akzeptieren, aber ich mag es nicht, dass die Anwendung dafür abstürzt.

Das Aktualisieren auf die neuesten System.IO.Pipelines hat das Problem für mich behoben.

@highfield sollten Sie Request.Form vermeiden und stattdessen ReadFormAsync verwenden

Hallo zusammen,

Wir haben das gleiche Problem in der Produktion, 2 S1-Instanzen. In unserer Umgebung scheint es mit Bursts in Verbindungen/Anfragen zu tun zu haben, siehe angehängtes Diagramm. Die Serverfehler in der Tabelle werden in dieser Ausgabe behandelt. Dh:

BadHttpRequestException: Reading the request body timed out due to data arriving too slowly

image

Dies ist nur zu sagen, dass wir immer noch sehr mit diesem Problem kämpfen.
Ich hoffe, dass zukünftige Versionen mit Spikes besser umgehen können.

Wir sehen das gleiche Problem in der Produktion, zusammen mit einem damit verbundenen BadHttpRequestException , das eine 400-Antwort auslöst: "Unerwartetes Ende des Anforderungsinhalts". Wir haben wenig Einblick in die Ursache dieses Problems, aber bereits 1 Anfrage/Sekunde mit einem 600-KB-Anfragetext löst es aus. Es scheint durch unzuverlässige Clientverbindungen, beispielsweise über mobile/zellulare 3G-Netzwerke, noch verschlimmert zu werden. Wir sehen keine entsprechenden Spitzen im CPU- oder Speicherverbrauch zu dem Zeitpunkt, zu dem diese auftreten.

Ich habe versucht, dieses Problem aufzuspüren, warum ASP.NET Core 2.1-Container auf K8s immer wieder abstürzen. Dachte, es würde die Anfrage abbrechen, war aber überrascht, dass es den Prozess zum Absturz bringt ...

Das Abbrechen der Anfrage führt nicht zum Absturz des Prozesses

Dies ist nur zu sagen, dass wir immer noch sehr mit diesem Problem kämpfen.
Ich hoffe, dass zukünftige Versionen mit Spikes besser umgehen können.

@clement911 Ich konnte dieses Problem in meinem Arbeitsprojekt beheben, indem ich die Verwendung von HTTP2 in der Kestrel-Konfiguration in Program.cs entfernt habe. Könnten Sie versuchen, über die Wirkung zu antworten - ich bin wirklich neugierig darauf, da in Unternehmen nur unser Projekt dieses Problem hat.

@Chakalaka11 Wir haben http 2 nicht aktiviert, aber könnten Sie bitte Ihren Code hier kopieren?

@clement911 Also, in den letzten Stunden hat das Team einige Änderungen vorgenommen, UseKestrel entfernt und ConfigureKestrel verwendet, um http2 erneut anzuhängen, funktioniert auch gut. Der Typ, der das Problem behoben hat, sagt, dass UseKestrel einige Standardkonfigurationen neu schreiben könnte und einen Fehler gemacht hat.
image

Vielleicht hilft das
Heute werde ich einige Regressionstests durchführen, um zu überprüfen, wie alles funktioniert und den Status zu aktualisieren, wenn es gut funktioniert.

UPD: Funktioniert prima.

Danke, aber ich zögere, Änderungen vorzunehmen, die nicht mit dem ursprünglichen Problem zu tun zu haben scheinen.
Wir möchten den In-Process-Host verwenden, aber ich sehe, dass er für das .net-Framework nicht verfügbar ist ...

Ok, wenn ich einige Updates zu diesem Fehler habe, lasse ich es Sie wissen. Hoffe du findest deine Lösung.

Ich möchte der "Party" beitreten.
In unserem Code machen wir:
C# await context.Request.Body.CopyToAsync(requestBodyStream);
Nichts synchron zum Anforderungsstream. Wir erhalten die Fehlermeldung "Zeitüberschreitung beim Lesen des Anforderungstexts, da die Daten zu langsam ankommen. Siehe MinRequestBodyDataRate."

Gibt es Erkenntnisse, die uns helfen könnten? Ich habe den obigen Thread gelesen und es hört sich so an, als ob die Ursache bisher unbekannt ist.

Also, ich bin auf der Suche dieses . Hat jemand versucht, einen niedrigeren Wert für MinRequestBodyDataRate einzustellen?
Außerdem befasst sich unsere Anwendung tatsächlich mit langsamen Netzwerkverbindungen (von Entwurf her). Vielleicht sollten wir das Front-End also anweisen, es erneut zu versuchen, wenn die Verbindung verbessert wird? Auch dies kann nur für bestimmte Arten von Apps gelten, und vielleicht gehören wir dazu.

Ich habe Folgendes in der Methode Startup.cs ConfigureServices versucht ...

services.Configure<KestrelServerOptions>(options =>
            {
                options.Limits.MinRequestBodyDataRate = new MinDataRate(1, TimeSpan.FromMinutes(5));
                options.Limits.MinResponseDataRate = new MinDataRate(1, TimeSpan.FromMinutes(5));
                options.Limits.MaxRequestBodySize = 100000000;
            });

Vor dieser Änderung hatte ich asp.net-Core-Container in Kubernetes, die 90x pro Stunde abstürzten/neustarten, wobei die letzte Meldung im Container-Protokoll der BadHttpRequestException Stack-Trace war. (Wie @davidfowl oben sagte, schien dies wirklich seltsam, weil ich erwarten würde, dass diese Ausnahme auf

Nachdem ich diese Änderung vorgenommen habe, hatte ich dieses Problem nicht. Wenn dieses Problem weiterhin besteht, war mein Plan, zu versuchen, eine Art Reverse-Proxy vor den Kestrel-Server zu stellen, der den langsamen eingehenden Datenstrom puffert, aber ich musste noch nicht an diesen Punkt gehen.

Schön zu hören, dass einige Lösungen finden.
Persönlich mag ich es nicht, Workarounds anzuwenden, die ich nicht wirklich verstehe, ohne das Problem zuerst zu verstehen.

Soweit ich weiß, ist der Fehler "Lesen des Anforderungstexts aufgrund zu langsam ankommender Daten" unter hoher Last nicht die eigentliche Ursache. Es ist eine Folge der Belastung des Systems.
Was ich immer noch nicht herausgefunden habe, ist, WARUM das System gestresst ist, da die Metriken gesund aussehen (meistens CPU, Speicher und Thread-Anzahl).
Ich vermute immer noch, dass dies dadurch verursacht werden könnte, dass der ThreadPool-Algorithmus Threads nicht schnell genug erstellt, wenn eine Spitze von Anfragen auftritt, aber soweit ich weiß, können wir diesen Algorithmus nicht optimieren.

Es wäre fantastisch, wenn das Kernteam von asp.net einen Spike-Stresstest für eine einfache Web-App erstellen könnte, um zu bestätigen, ob tatsächlich ein Engpass auf Thread-Pool-Ebene oder anderswo vorliegt.

Wir mussten unsere azurblauen Instanzen hochskalieren, obwohl sie fehlerfrei aussehen, nur weil wir unzählige dieser Fehler sowie die Fehler "Verbindung zwangsweise geschlossen" und "Verbindungsverarbeitung wurde abnormal beendet" erhalten.

Das passiert bei uns in
Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1MessageBody.<PumpAsync>
angerufen von
Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.MessageBody.<ReadAsync>
angerufen von
Microsoft.AspNetCore.WebUtilities.FormReader.<ReadFormAsync>
angerufen von
Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgeryTokenStore.<GetRequestTokensAsync>
was bedeutet, dass es bei csrf check passiert.

Mehr Stacktrace:
Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Reading the request body timed out due to data arriving too slowly. See MinRequestBodyDataRate. at Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException.Throw(RequestRejectionReason reason) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1MessageBody.<PumpAsync>d__4.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.IO.Pipelines.PipeCompletion.ThrowLatchedException() at System.IO.Pipelines.Pipe.GetReadResult(ReadResult& result) at System.IO.Pipelines.Pipe.GetReadAsyncResult() at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.MessageBody.<ReadAsync>d__21.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpRequestStream.<ReadAsyncInternal>d__21.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.IO.StreamReader.<ReadBufferAsync>d__97.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.IO.StreamReader.<ReadAsyncInternal>d__64.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.WebUtilities.FormReader.<BufferAsync>d__41.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.WebUtilities.FormReader.<ReadNextPairAsyncImpl>d__34.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.WebUtilities.FormReader.<ReadFormAsync>d__43.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Http.Features.FormFeature.<InnerReadFormAsync>d__18.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgeryTokenStore.<GetRequestTokensAsync>d__3.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgery.<ValidateRequestAsync>d__9.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.ValidateAntiforgeryTokenAuthorizationFilter.<OnAuthorizationAsync>d__3.MoveNext() -

Das hatten wir jetzt einmal. Die Operation sendet eine ID-Zeichenfolge und eine Antwortzeichenfolge zusammen mit einigen Cookies. Also wirklich kleiner Körper.

2019-05-09 11:31:50.9573|30131|TRACE|9.5.3.0|189|Base|BaseController.QuestionsSaveAnswer|27 ms|
2019-05-09 11:32:17.6812|30131|TRACE|9.5.3.0|97 |Base|BaseController.QuestionsSaveAnswer|24 ms|
2019-05-09 11:32:27.2114|30131|ERROR|9.5.3.0|106|Base|BaseController.GetErrorMessage|Error Page Path=/questionsSaveAnswer, error = Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Reading the request body timed out due to data arriving too slowly. See MinRequestBodyDataRate.
   at Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException.Throw(RequestRejectionReason reason)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1MessageBody.PumpAsync()
   at System.IO.Pipelines.PipeCompletion.ThrowLatchedException()
   at System.IO.Pipelines.Pipe.GetReadResult(ReadResult& result)
   at System.IO.Pipelines.Pipe.GetReadAsyncResult()
   at System.IO.Pipelines.Pipe.DefaultPipeReader.GetResult(Int16 token)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.MessageBody.ReadAsync(Memory`1 buffer, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpRequestStream.ReadAsyncInternal(Memory`1 buffer, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.WebUtilities.FileBufferingReadStream.ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.WebUtilities.StreamHelperExtensions.DrainAsync(Stream stream, ArrayPool`1 bytePool, Nullable`1 limit, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Mvc.Formatters.JsonInputFormatter.ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
   at Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder.BindModelAsync(ModelBindingContext bindingContext)
   at Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(ActionContext actionContext, IModelBinder modelBinder, IValueProvider valueProvider, ParameterDescriptor parameter, ModelMetadata metadata, Object value)
   at Microsoft.AspNetCore.Mvc.Internal.ControllerBinderDelegateProvider.<>c__DisplayClass0_0.<<CreateBinderDelegate>g__Bind|0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---

Wir führen prozessinternen ASP Net Core 2.2.0 auf IIS aus. Im Log die Zahl vor |Base| ist die Thread-ID.

Endlich haben wir eine Lösung gefunden!
Wie wir vermutet hatten, lag es daran, dass der Threadpool die Threads nicht schnell genug hochgefahren hat.
Wir haben die folgenden zwei Zeilen hinzugefügt und das Problem wurde sofort vollständig behoben:

ThreadPool.SetMaxThreads(32_767, 2_000);
ThreadPool.SetMinThreads(1_000, 1_000);

Der Hochlaufalgorithmus für den Threadpool ist sehr konservativ, ebenso wie die standardmäßigen Mindestthreads.
Ich bin mir nicht sicher, ob wir die Worker-Threads hochfahren mussten. Eventuell würden die io-Threads ausreichen, da die CPU bei einer sehr geringen prozentualen Auslastung.
Meine Lesart ist also, dass die IO-Anforderungen intern vom Thread-Pool unter Spike in die Warteschlange gestellt wurden.
Ich bin froh, dass wir eine Lösung gefunden haben, aber obwohl die Lösung winzig ist, hat es eine Weile gedauert, sie zu finden.

IMO sind die Standardeinstellungen zu konservativ.
Für eine normale Website, die möglicherweise gut funktioniert, gibt es jedoch bei einem API- oder Webhook-Empfänger Spitzen, die viel schneller als 2 Threads pro Sekunde hochfahren müssen.

Es hilft nicht, dass der ThreadPool undurchsichtig ist und die Dokumentation sehr begrenzt ist.

cc @stephentoub @kouvel

Dies ist ein ausgezeichneter Artikel, der beschreibt, was dies verursacht und warum die Erhöhung des Minimums nur eine Notlösung sein sollte
https://blogs.msdn.microsoft.com/vancem/2018/10/16/diagnosing-net-core-threadpool-starvation-with-perfview-why-my-service-is-not-saturating-all-cores- oder-scheint zu stehen/

Ja danke, das hatte ich gelesen und es ist in der Tat ein toller Artikel.
Es besagt, dass das Erhöhen des Minimums an Threads eine Notlösung ist, wenn der Thread-Aushunger durch blockierende Aufrufe verursacht wird .
Aber in unserem Fall blockieren wir nie. Wir verwenden nur asynchrone IO-APIs.
Soweit ich das beurteilen kann, besteht die einzige Möglichkeit, einen plötzlichen Burst zu bewältigen, darin, das Minimum an Threads zu erhöhen.
Wir haben jetzt auf eine billigere azurblaue Instanz herunterskaliert und von 4 Instanzen auf 2 Instanzen hochskaliert und erhalten keinen einzigen Fehler mehr. Es ist ziemlich bemerkenswert!
Interessanterweise hat sich die Anzahl der Threads nicht wesentlich erhöht, aber sie kann viel schneller als zuvor hochgefahren werden.
Beachten Sie auch, dass die Anzahl der Threads unter dem von uns festgelegten Minimum von 1.000 liegt, da der Thread-Pool sie nicht erzeugt, es sei denn, es warten Elemente in der Warteschlange.
Zumindest ist das mein Verständnis.
Es kann nützlich sein, Thread-Pools instanziieren zu können und ihnen verschiedene Optionen für verschiedene Workloads zu geben...

Ich bin extrem überrascht, dass Sie Hunger ohne Blockieren sehen, selbst für Ausbrüche.

@clement911 versuchen https://github.com/benaadams/Ben.BlockingDetector zu verwenden, um zu sehen, welcher Framework-Code blockiert

Die einzigen Dinge, von denen mir bekannt ist, dass sie Thread-Aushungern verursachen würden, sind das Blockieren oder lang laufende CPU- / speichergebundene Arbeitselemente im Thread-Pool. @clement911 Da Sie erwähnt haben, dass die CPU-Auslastung gering ist, würde ich ohne weitere Informationen vermuten, dass eine Blockierung auftritt.

Das Blockieren muss nicht unbedingt von synchroner IO erfolgen. Dies kann eine Sperre sein, die länger als erwartet gehalten wird, Weiterleitungsabhängigkeiten in Aufgaben in der Warteschlange (eine Aufgabe plant eine andere Aufgabe und wartet synchron auf deren Abschluss) oder allgemein jede synchrone Aktivität, die einige Zeit in Anspruch nimmt.

Endlich haben wir eine Lösung gefunden!
Wie wir vermutet hatten, lag es daran, dass der Threadpool die Threads nicht schnell genug hochgefahren hat.
Wir haben die folgenden zwei Zeilen hinzugefügt und das Problem wurde vollständig und sofort behoben

Dies ist die vorgeschlagene Problemumgehung, wenn die Ursache nicht behoben werden kann. Es kann durchaus eine geeignete Lösung sein, wenn aufgrund von Mängeln im Thread-Pool bei Workitems, die für relativ lange Zeiträume blockieren, eine Blockierung auftritt. Die Lösung kann kontraproduktiv sein, wenn keine Blockierung auftritt. Da dies nicht das ist, was Sie sehen, klingt es wie eine Blockierung.

Es kann hilfreich sein, sich die Blockierung anzusehen, die wahrscheinlich bei einem PerfView- Trace auftritt, einschließlich Thread Time (einschließlich Kontextwechselereignissen und Zeitaufwand für das Blockieren).

Danke, ich freue mich über die Kommentare.

Vor einiger Zeit wussten wir nicht, was wir mit diesem Problem anfangen sollten, aber wir wussten, dass es damit zusammenhängt, dass unsere Webhook-Empfänger-Controller-Aktion viele Anfragen erhielt.
Daher haben wir eine weitere separate Azure-App-Dienstinstanz bereitgestellt, die speziell zum Empfangen von Webhooks dient.
Wir haben also einen asp.net-Kerndienst mit einem einzigen Controller und einer einzelnen aufgerufenen Aktion.
(Wir haben den Rest des Codes belassen, aber die einzige aufgerufene Aktion ist die Webhook-Aktion, die keine Benutzeranfragen bedient oder Jobs ausführt.)
Es überprüft lediglich die Header, um sicherzustellen, dass die Anforderung authentisch ist, und speichert die Nutzlast mit einem asynchronen Aufruf in einer SQL Azure-Datenbank.
Das ist es.
Deshalb bin ich ziemlich zuversichtlich, dass wir keine Blockaden haben.

Zu Ihrem Punkt verbessert Async die Skalierbarkeit, aber es gibt immer noch eine Grenze, wie viele Anfragen verarbeitet werden können, oder?
In unserem Fall kann unser Spike sehr plötzlich und sehr groß sein.
Wenn ein Benutzer einen CSV-Import in Shopify initiiert, sendet Shopify uns sofort 1 Anfrage pro Zeile.
Wir könnten in einer Minute 1.000 Anfragen erhalten, die eine einzelne Instanz erreichen. Ist es unter diesen Bedingungen nicht möglich, dass der Threadpool Anfragen in die Warteschlange stellt?

Bei der Ausführung auf IIS gibt es eine Anforderungswarteschlange tief im Kernel (http.sys), aber in ASP.NET Core findet keine Anforderungswarteschlange statt. Die einzige Warteschlange ist die Thread-Pool-Warteschlange. Die Ausnahme, die Sie sehen, kommt von Kestrel (also gehe ich davon aus, dass Sie das IIS-Modul außerhalb des Prozesses verwenden). Wenn Sie das Problem noch reproduzieren können, wäre eine Spur großartig. Wir fügen nur Leistungsindikatoren in .NET Core 3.0 hinzu, was Ihnen im Moment keine Hilfe ist, aber diese Art von Sache sollte in Zukunft mit diesen Leistungsindikatoren offensichtlicher sein.

Übrigens bezog ich mich auf die Thread-Pool-Warteschlange.

Vielleicht können Sie die Anfragen von Shopify in Ihre eigene Hintergrundwarteschlange stellen und nacheinander verarbeiten. Auf diese Weise haben Sie nie die Spitze.
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.2

Ich habe das in einem ähnlichen Szenario getan, in dem der Benutzer eine Tabelle hochlädt und dann pro Zeile viel Arbeit erledigt werden muss. Gerne den Code irgendwo teilen.

@vankampenp
Das Problem ist, dass wir sicherstellen müssen, dass die Webhook-Anfragen bestehen bleiben, bevor wir HTTP 200 an Shopify beantworten können.
Wenn wir es einfach in eine Speicherwarteschlange stellen und die App aus irgendeinem Grund neu startet, würden wir die Anfragen verlieren.

@clement911 Ich kenne deine spezielle Situation nicht. In unserem Szenario behalten wir einen Schlüssel bei und schieben ihn dann in die Warteschlange, die ein Flag setzt, sobald dies erledigt ist. Wir führen täglich einen Catch-All durch, um zu sehen, ob es Schlüssel ohne Flagge gibt.
Dies ist nur sinnvoll, wenn die zu erledigenden Arbeiten lange dauern.

selbst wenn es sich nur um einen Schlüssel handelt, müssen Sie zumindest etwas beibehalten, damit IO benötigt wird.

Wir hatten das gleiche Problem und haben es gelöst, indem wir das Lesen des Anfragetexts aus dem Controller verschoben haben.

In unserem Fall hatten wir eine REST-API mit hohem Volumen, bei der zuerst der Request-Body mit _HttpRequestStreamReader_ innerhalb des Controllers gelesen wurde. Unter stärkerer Last sahen wir alle paar Tage diesen Fehler:

BadHttpRequestException: Zeitüberschreitung beim Lesen des Anfragetexts, da Daten zu langsam ankommen

Und während dieser Zeit war auch ein starker Durchsatzeinbruch zu verzeichnen. Dies geschah zufällig und erholte sich nach etwa 10 Minuten von selbst.

Mit dem BlockingDetector-Tool konnten wir beim Lesen des Anfragetexts irgendwo im Framework minimale Blockierungen identifizieren.

Aus diesem Grund haben wir einen InputFormatter erstellt, der es erlaubt, den json-formatierten Anfragetext als String zu lesen und den Anfragetext als Parameter im Controller wie folgt abzurufen:

public async Task<IActionResult> ProcessAsync([FromBody]string jsonBody = null)

Nach Anwendung dieses Workarounds hat der BlockingDetector keine Blockierung mehr erkannt und wir hatten jetzt seit drei Wochen keine BadHttpRequestExceptions mehr.

Mit dem BlockingDetector-Tool konnten wir beim Lesen des Anfragetexts irgendwo im Framework minimale Blockierungen identifizieren.

Sie können die Ergebnisse davon teilen?

Ja, das war die Ausgabe des Tools:
image

Und hier der vorherige Controller-Code, der dies ausgelöst hat:
using (HttpRequestStreamReader reader = new HttpRequestStreamReader(Request.Body, Encoding.UTF8)) { _jsonBody = await reader.ReadToEndAsync(); }

using (HttpRequestStreamReader reader = new HttpRequestStreamReader(Request.Body, Encoding.UTF8)) { _jsonBody = wait reader.ReadToEndAsync(); }

Haben Sie diesen Code in Ihren Controller geschrieben?

Ja, das war der Code mit der Blockierung.

@berndku warum wird eine manuelle JSON-

@davidfowl : Leistung, nicht alle unsere Anfragen erfordern eine vollständige

@davidfowl : Leistung, nicht alle unsere Anfragen erfordern eine vollständige

Leistung ist überlastet? Besserer Durchsatz gegen Speichernutzung? Das Puffern des gesamten Körpers im Speicher bedeutet, dass Sie diese temporäre Zeichenfolge nur haben, um sie in ein anderes Objekt umzuwandeln. Die eingebauten Formatierer blockieren nicht, sie tun alles, um die Pufferung in einem einzigen zusammenhängenden Speicherblock (wie einem String oder Byte[]) zu vermeiden.

Unabhängig davon werde ich ein Problem für das ReadToEndAsync-Verhalten melden https://github.com/aspnet/AspNetCore/issues/13834

Danke für die Einreichung des Problems!

Verstehen Sie Ihren Standpunkt zu integrierten Formatierern und Puffern vollständig, aber wir müssen jeden Körper innerhalb des Controllers vollständig verfolgen, egal ob wir ihn deserialisieren oder nicht. Wir brauchen also sowieso eine String-Darstellung des Körpers.

Verstehen Sie Ihren Standpunkt zu integrierten Formatierern und Puffern vollständig, aber wir müssen jeden Körper innerhalb des Controllers vollständig verfolgen, egal ob wir ihn deserialisieren oder nicht. Wir brauchen also sowieso eine String-Darstellung des Körpers.

https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/master/AspNetCoreGuidance.md#avoid -reading-large-request-bodies-or-response-bodies-into-memory

😄

@davidfowl Dieser Link bietet Kontext dazu, _warum_ man dies nicht tun sollte (großartig! 😃), aber nicht _was stattdessen zu tun ist_ (oh 😞). Nehmen wir an, Sie müssen beliebige Dateikörper in einer Anforderung akzeptieren und diesen beliebigen Dateianforderungskörper an ein anderes System senden, beispielsweise an eine Datenbank oder eine Nachrichtenwarteschlange. Wie vermeiden Sie das Einlesen dieser Anfrage in den Speicher?

Dies ist zwar keine gute Vorgehensweise, aber die Anforderungen umfassen etwa 20-40 KB, sodass keine Probleme mit der LOH-Fragmentierung zu erwarten sind. Ich habe vor einiger Zeit empfohlen, das Bodytracing an eine andere Stelle in der Architektur zu verlegen, aber das wird länger dauern und wir brauchten eine kurzfristige Lösung.

Da es sich bei #13834 um _HttpRequestStreamReader_ handelt, habe ich unseren benutzerdefinierten Formatierer (Code unten) erneut überprüft, aber dort haben wir einfach _StreamReader_ verwendet. Könnte das der Grund sein, warum wir die Blockierung mit dem benutzerdefinierten Formatierer nicht sehen?

Wenn ja, bedeutet das, dass wir _HttpRequestStreamReader_ vermeiden sollten, bis das Problem behoben ist?

 public class RawJsonBodyInputFormatter : InputFormatter
    {
        public RawJsonBodyInputFormatter()
        {
            this.SupportedMediaTypes.Add("application/json");
        }

        public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
        {
            var request = context.HttpContext.Request;
            using (var reader = new StreamReader(request.Body))
            {
                var content = await reader.ReadToEndAsync();
                return await InputFormatterResult.SuccessAsync(content);
            }
        }

        protected override bool CanReadType(Type type)
        {
            return type == typeof(string);
        }
    }

mm, sorry, wenn das nicht zusammenhängt, aber ich erhalte diese Ausgabe vom Blockingmonitor, wenn IIS neu gestartet wird. Es kommt mehrfach vor. Ich verwende den Taghelper asp-append-version. Nachdem dies vorübergehend entfernt wurde, wird der blockierende Anruf nicht mehr gemeldet.

2019-09-10 15:06:25.0604|35.230.16.48||WARN |9.9.19.0|39 |Base|BlockingMonitor.BlockingStart|Blocking-Methode wurde aufgerufen und blockiert, dies kann zu Threadpool-Aushungern führen.
at System.IO.FileStream.Read(Byte[] Array, Int32 Offset, Int32 Anzahl)
at System.Security.Cryptography.HashAlgorithm.ComputeHash(Stream inputStream)
bei Microsoft.AspNetCore.Mvc.Razor.Infrastructure.DefaultFileVersionProvider.GetHashForFile(IFileInfo fileInfo)
bei Microsoft.AspNetCore.Mvc.Razor.Infrastructure.DefaultFileVersionProvider.AddFileVersionToPath(PathString requestPathBase, String path)
bei Microsoft.AspNetCore.Mvc.TagHelpers.ScriptTagHelper.Process(TagHelperContext-Kontext, TagHelperOutput-Ausgabe)
bei Microsoft.AspNetCore.Razor.TagHelpers.TagHelper.ProcessAsync(TagHelperContext-Kontext, TagHelperOutput-Ausgabe)
bei Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner.RunAsync(TagHelperExecutionContext ExecutionContext)
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start TStateMachine
bei Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner.RunAsync(TagHelperExecutionContext ExecutionContext)
bei AspNetCore.Views_Shared__Layout.<>c__DisplayClass14_0.<b__1>d.MoveNext()
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start TStateMachine
bei AspNetCore.Views_Shared__Layout.<>c__DisplayClass14_0.b__1()
bei Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperExecutionContext.SetOutputContentAsync()
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start TStateMachine
bei Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperExecutionContext.SetOutputContentAsync()
bei AspNetCore.Views_Shared__Layout.ExecuteAsync()
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start TStateMachine
bei AspNetCore.Views_Shared__Layout.ExecuteAsync()
bei Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage-Seite, ViewContext-Kontext)
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start TStateMachine
bei Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage-Seite, ViewContext-Kontext)
bei Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync (IRazorPage-Seite, ViewContext-Kontext, boolescher AufrufViewStarts)
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start TStateMachine
bei Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync (IRazorPage-Seite, ViewContext-Kontext, Boolescher AufrufeViewStarts)
bei Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderLayoutAsync(ViewContext context, ViewBufferTextWriter bodyWriter)
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start TStateMachine
bei Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderLayoutAsync(ViewContext context, ViewBufferTextWriter bodyWriter)
bei Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext-Kontext)
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start TStateMachine
bei Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext-Kontext)
bei Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, String contentType, Nullable 1 statusCode) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, String contentType, Nullable 1 statusCode)
at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ActionContext actionContext, IView-Ansicht, ViewDataDictionary viewData, ITempDataDictionary tempData, String contentType, Nullable 1 statusCode) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ActionContext actionContext, IView view, ViewDataDictionary viewData, ITempDataDictionary tempData, String contentType, Nullable 1 statusCode)
bei Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor.ExecuteAsync(ActionContext-Kontext, ViewResult-Ergebnis)
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start TStateMachine
bei Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor.ExecuteAsync(ActionContext-Kontext, ViewResult-Ergebnis)
bei Microsoft.AspNetCore.Mvc.ViewResult.ExecuteResultAsync(ActionContext-Kontext)
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start TStateMachine
bei Microsoft.AspNetCore.Mvc.ViewResult.ExecuteResultAsync(ActionContext-Kontext)
bei Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeResultAsync(IActionResult-Ergebnis)
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start TStateMachine
bei Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeResultAsync(IActionResult-Ergebnis)
bei Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.ResultNext TFilter,TFilterAsync
bei Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResultFilterAsync TFilter,TFilterAsync
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start TStateMachine
bei Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResultFilterAsync TFilter,TFilterAsync
bei Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.ResultNext TFilter,TFilterAsync
bei Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeResultFilters()
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start TStateMachine
bei Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeResultFilters()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
bei Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext ExecutionContext, ContextCallback Callback, Object state)
bei System.Runtime.CompilerServices.AsyncTaskMethodBuilder 1.AsyncStateMachineBox 1.MoveNext()
bei System.Runtime.CompilerServices.TaskAwaiter.<>c.b__12_0(Aktion innerContinuation, Aufgabe innerTask)
bei System.Threading.QueueUserWorkItemCallback 1.<>c.<.cctor>b__6_0(Object state) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.QueueUserWorkItemCallback 1.ExecuteWorkItem()
bei System.Threading.ThreadPoolWorkQueue.Dispatch()

@alastairs

@davidfowl Dieser Link liefert einen Kontext, warum man dies nicht tun sollte (großartig! 😃), aber nicht, was stattdessen zu tun ist (oh 😞). Nehmen wir an, Sie müssen beliebige Dateikörper in einer Anforderung akzeptieren und diesen beliebigen Dateianforderungskörper an ein anderes System senden, beispielsweise an eine Datenbank oder eine Nachrichtenwarteschlange. Wie vermeiden Sie das Einlesen dieser Anfrage in den Speicher?

Indem Sie es in <85K-Puffer aufteilen und es dann Stück für Stück übertragen. Dies ist einer der Tricks, die Stream verwendet, um Daten von einem Stream in einen anderen zu kopieren https://github.com/dotnet/corefx/blob/ee9995f31b684a0c6e5488eceb2500bf0057da89/src/Common/src/CoreLib/System/IO/Stream.cs#L31 -L34.

In Kombination mit dem Pooling dieser Puffer würde dies insgesamt zu einer besseren Leistung führen, da der große Objektheap nicht aggressiv umherwirbelt. Außerdem besteht ein potenzielles Sicherheitsrisiko darin, einen Endpunkt bereitzustellen, der alles in den Speicher einliest und auf ein anderes System überträgt. Jeder Ort, an dem Sie beliebige Client-Eingaben vornehmen können, ist ein potenzieller Ort, an dem Clients möglicherweise einen Denial-of-Service verursachen können, wenn Sie nicht aufpassen.

@berndku

Da es sich bei #13834 um HttpRequestStreamReader handelt, habe ich unseren benutzerdefinierten Formatierer (Code unten) erneut überprüft, aber dort haben wir einfach StreamReader verwendet. Könnte das der Grund sein, warum wir die Blockierung mit dem benutzerdefinierten Formatierer nicht sehen?

Ja, StreamReader überschreibt die richtigen Methoden.

.UseKestrel(opt => { opt.Limits.MinRequestBodyDataRate = null; })
Könnte das funktionieren?

Habe das gleiche Problem, wenn zwei Dienste miteinander kommunizieren (innerhalb von localhost). @HengzheLi das hat mir nicht geholfen...

Habe das gleiche Problem

Hallo zusammen,
Wir starten ein neues Projekt, bei dem erneut Webhooks zur weiteren Verarbeitung in der Datenbank gespeichert werden.
Wir erwarten kleine Nutzlasten und aber eine hohe Anzahl von Anfragen.

Ist der folgende Code optimal?

[HttpPost]
        public async Task<OkResult> Webhook()
        {
            var body = await new HttpRequestStreamReader(Request.Body, Encoding.UTF8).ReadToEndAsync();
            //save to Db asynchronously?
            return Ok();
        }

Ich erhalte auch diesen Fehler, aber ich denke, mein Problem ist hier:
blob.UploadFromByteArrayAsync(fileByteArray, 0, fileByteArray.Length).Wait();
vs
await blob.UploadFromByteArrayAsync(fileByteArray, 0, fileByteArray.Length);

Die Verwendung von Wait() bewirkt, dass der Upload den Thread blockiert, während die Verwendung von await nicht tut.

@clement911 Das ist eine gute Möglichkeit, Webhooks zu verarbeiten, und es wäre großartig, wenn Ihre Verarbeitung dann von einer Ereigniswarteschlange anstelle des Webservers erfolgt.

Mein Problem besteht darin, eine URL zu erhalten, sie herunterzuladen und dann hochzuladen! Es ist dumm, das alles in der Webanfrage zu tun. Das Beheben des Wait() Problems wird also strittig sein, sobald ich diesen Code überarbeite, um nur zu überprüfen, ob die Bild-URL in Ordnung ist, und dann die tatsächlichen DB-Speicherungen und Bild-Uploads in einen Ereignisprozessor speichere.

Aber danke, dass Sie mit diesem Thema Schritt halten!

Zu Ihrer Information, seit dem Zeitpunkt meines Beitrags hier habe ich ein neues Projekt mit Asp Net Core 2.2 (vorher war 2.1) gestartet. Außerdem läuft die App jetzt auf Ubuntu 18 (vorher war auf 14).

Seitdem ist niemand mehr gescheitert.

@davidfowl Ist es der folgende Code, um einen Upload an einen anderen Dienst weiterzuleiten? Jeder Upload besteht im Wesentlichen aus einem Bild (10-15 MB) und wenigen Schlüssel-Wert-Paaren.

    [HttpPost("photo/post3")]
    public async Task PostV3Async()
    {
        IFormCollection fcoll = await this.Request.ReadFormAsync();

        var content = new MultipartFormDataContent();
        foreach (var pair in fcoll)
        {
            content.Add(new StringContent(pair.Value), pair.Key);
        }
        foreach (IFormFile ff in fcoll.Files)
        {
            byte[] data;
            using (var br = new BinaryReader(ff.OpenReadStream()))
            {
                data = br.ReadBytes((int)ff.OpenReadStream().Length);
            }
            content.Add(new ByteArrayContent(data), ff.Name, ff.FileName);
        }

        var client = this._httpClientFactory.CreateClient(ClientName);
        await client.PostAsync("/photo/post3", content);

        this.Ok();
    }

Ich frage mich immer noch, was der beste Weg ist, den gesamten Anforderungstext asynchron zu lesen.

Wir haben await new HttpRequestStreamReader(Request.Body, Encoding.UTF8).ReadToEndAsync();
aber nach dem Upgrade auf Net Core 3.1 bekommen wir dieses Problem: https://github.com/aspnet/AspNetCore/issues/13834

Da der Fix für .net 5.0 geplant ist, was ist der richtige Weg für .net 3.1?

Irgendein Commit für das Problem?

Da dieses Problem von WhiteSource als mittlere Sicherheitslücke eingestuft wurde, kann mir jemand eine kurze Zusammenfassung des Exploits geben? Dies ist ein SEHR langer Thread, der bis 2018 zurückreicht. Danke.

Ich habe das gleiche Problem

Habe auch das gleiche Problem.

Für diejenigen, die das Problem immer noch haben, haben Sie meinen Beitrag gelesen? https://github.com/dotnet/aspnetcore/issues/4707#issuecomment -557538742

Wie heute hatte ich seit über einem Jahr KEINE Probleme auf zwei verschiedenen Servern.

Bei jedem Update zu diesem Problem stehe ich auch vor der gleichen Ausnahme. Die Anfrage ist ziemlich klein, aber Turmfalke wirft 408 mit der Nachricht "Anfrage kommt zu langsam an".

"message": "Reading the request body timed out due to data arriving too slowly. See MinRequestBodyDataRate.", "exception": {"Type":"Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException", "StatusCode":408, "TargetSite":"Void Throw(Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.RequestRejectionReason)", "StackTrace":"   at Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException.Throw(RequestRejectionReason reason)\r\n   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1ContentLengthMessageBody.ReadAsyncInternal(CancellationToken cancellationToken)\r\n   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpRequestStream.ReadAsyncInternal(Memory`1 buffer, CancellationToken cancellationToken)\r\n   at Microsoft.AspNetCore.WebUtilities.FileBufferingReadStream.ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)\r\n   at Microsoft.AspNetCore.WebUtilities.StreamHelperExtensions.DrainAsync(Stream stream, ArrayPool`1 bytePool, Nullable`1 limit, CancellationToken cancellationToken)\r\n   at Microsoft.AspNetCore.Mvc.Formatters.NewtonsoftJsonInputFormatter.ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)\r\n   at Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder.BindModelAsync(ModelBindingContext bindingContext)\r\n   at Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(ActionContext actionContext, IModelBinder modelBinder, IValueProvider valueProvider, ParameterDescriptor parameter, ModelMetadata metadata, Object value)\r\n   at Microsoft.AspNetCore.Mvc.Controllers.ControllerBinderDelegateProvider.<>c__DisplayClass0_0.<<CreateBinderDelegate>g__Bind|0>d.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\r\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\r\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ExceptionContextSealed context)\r\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)\r\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\r\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)\r\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)\r\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\r\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)\r\n   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)\r\n
    [HttpPost]
    [ProducesResponseType(typeof(BaseResponse<bool>), 1)]
    public async Task<BaseResponse> SyncMsg()
    {

        string data = string.Empty;
        try
        {
            using (var sr = new StreamReader( Request.Body))
            {
                data = await sr.ReadToEndAsync();

                //todo: lenth more then 1024, Decompress
                data = StringHandleHelper.Decompress(data);

                var url = ConfigurationManager.GetValue("CallBackUrl");

pragma warning disable CS4014 // Da dieser Aufruf nicht wartet, wird die aktuelle Methode weiterhin ausgeführt, bevor der Aufruf abgeschlossen ist

                Task.Factory.StartNew((obj) =>
                {
                     _messageService.SyncMsg((string)obj, url);
                }, data);

pragma warning restore CS4014 // Da dieser Aufruf nicht wartet, wird die aktuelle Methode weiterhin ausgeführt, bevor der Aufruf abgeschlossen ist

                var j_data = JToken.Parse(data);
                var type = j_data["type"].ToObject<PcWxMsgType>();
                var wxid = j_data["wxid"].ToObject<string>();

                return Response2WeChatRobot(wxid, type);
            }
        }
        catch (Exception ex)
        {
            return new BaseResponse() { Code = -2, Msg = "站点:" + ex.Message };
        }
    }

ich

Ich habe alle Diskussionen gesehen. Gibt es jetzt eine effektive Lösung?

Wird das in der kommenden Version behoben?

Hallo zusammen,
Wir starten ein neues Projekt, bei dem erneut Webhooks zur weiteren Verarbeitung in der Datenbank gespeichert werden.
Wir erwarten kleine Nutzlasten und aber eine hohe Anzahl von Anfragen.

Ist der folgende Code optimal?

[HttpPost]
        public async Task<OkResult> Webhook()
        {
            var body = await new HttpRequestStreamReader(Request.Body, Encoding.UTF8).ReadToEndAsync();
            //save to Db asynchronously?
            return Ok();
        }

@clement911 Wenn der Wechsel zur DB-Verarbeitung zuerst das ReadToEndAsync-Problem lösen würde?

Oder haben Sie versucht, eine strukturierte Klasse zu verwenden, anstatt das Objekt stattdessen aus Stream zu lesen?

Ok, ich habe diesen Thread vor einer Stunde angefangen zu lesen und ich höre am Ende noch keine Lösung. Was ist jetzt die Lösung für .NEt 3.1, ich habe das gleiche Problem?

Das liegt daran, dass sich aus dem Thema viele verschiedene Probleme entwickelt haben 😄. Für dieses Problem gibt es keine Reproduktion und es hängt von der Umgebung und der Clientgeschwindigkeit ab (zumindest das ursprüngliche Problem). Wir wissen, dass es irgendwo ein Problem gibt, aber es ist nicht klar, wie wir mit den hier bereitgestellten Informationen vorankommen.

@Gopichandar Ich bin darauf

  • Refraktor die meisten Methoden mit await + async Task method() pattern

    scheint in unserem Fall die Hauptursache zu sein, die dadurch verursacht wird, dass alle CPUs für wartende Tasks (meist DB/IO-bezogen) zum Abschluss verwendet werden, und dies führt zu der Ausnahme mit BadHttpRequestException: Das Lesen des Request-Bodys hat eine Zeitüberschreitung aufgrund zu langsam ankommender Daten

@davidfowl Ich glaube, ich konnte dieses Problem immer wieder reproduzieren, als ich meinen eigenen Code

Die längere Geschichte:
Hatte einige Millionen PUT-Aufrufe an unsere Server und etwa 0,08% von ihnen schlugen auch nach mehreren Wiederholungsversuchen fehl – ​​alle hatten die gleiche Nachricht auf der Serverseite: “Reading the request body timed out due to data arriving too slowly. See MinRequestBodyDataRate” . Sicher war der Server während der vielen Anrufe nicht untätig. Aber am Tag danach war der Server fast im Leerlauf, und ich konnte die fehlgeschlagenen Anrufe entgegennehmen und sie zu diesem Zeitpunkt als einzelne Anforderung erneut ausführen. Zu meiner großen Überraschung war es konsistent, dass die Nutzlast fehlgeschlagen ist und ich immer noch dieselbe Fehlermeldung in unseren Serverprotokollen sehe.
Hatte auch versucht, den Timeout zu erhöhen, um dem Server Zeit zu geben, einen freien Thread zu finden, aber immer noch die gleichen Fehler nach 10 Sekunden statt der Standardeinstellung von 5 Sekunden:
options.Limits.MinRequestBodyDataRate = new MinDataRate(120, TimeSpan.FromSeconds(10))

Nach stundenlangen Nachforschungen fand ich heraus, dass ich zwei Dinge ändern konnte, damit der Anruf erfolgreich war:
1) Ciso-VPN trennen.
Das VPN teilt die Nutzlast in kleinere TCP-Pakete auf (hier sind es 5 Pakete). Es ist immer das letzte Paket, das erneut übertragen werden muss. Siehe Bild, ".135" ist mein PC und ".57" ist Server. (Wenn ich vom VPN getrennt bin, wird die Nutzlast in einem einzigen TCP-Paket gesendet). War auch bei den paar Millionen Anrufen mit VPN verbunden.
image

2) Ändern Sie die Nutzlastgröße mit 1 Byte – fügen Sie entweder ein Zeichen zur json-Nutzlast hinzu oder entfernen Sie es (natürlich in einem Feld, das nicht serverseitig analysiert wird)
Die Nutzlast wird immer noch in 5 TCP-Pakete aufgeteilt - aufgrund der Verbindung mit VPN.

Die Nutzlast beträgt ~6 KB.
Wir sehen diesen Fehler nicht nur auf meinem PC mit VPN, sondern auch auf Anfragen unserer Endkunden auf der ganzen Welt ohne VPN. Aber diese Untersuchung ist die nächste, die ich hatte, um diese Fehlermeldung zu debuggen, zu analysieren und zu verstehen, die wir in unseren Serverprotokollen sehen.

Ich glaube nicht, dass es sich auf der Serverseite um ein Thread-Pool-Problem handelt, da ich es immer wieder reproduzieren kann und eine Änderung der Nutzlastgröße um 1 Byte das Ergebnis beeinflussen kann. Und neulich hat der Server viel mehr Anfragen bearbeitet.

Ich hatte die Fehlermeldung “Reading the request body timed out due to data arriving too slowly. See MinRequestBodyDataRate” sowohl bei Anfragen zu Routen mit einfacher Aspnet-Kernmodellbindung [FromBody] als auch bei Routen, die den Körper selbst lesen, await Request.Body.ReadAsBytesAsync() .

Die Frage ist also, was in aspnetcore dieses Problem verursachen kann? und wie teile ich mehr Daten mit euch?

Es scheint, als hätte ich einen Workaround gefunden, bis dies behoben ist.
Ich hatte die gleichen Probleme wie @rasmus-s . Es funktionierte weiter, bis die TCP-Pakete in separate Pakete aufgeteilt wurden, die (für mich) genau 1398 Bytes waren. Addiert man den Overhead, kommt das seltsamerweise den ~1500 Bytes eines Ethernet-Frames nahe.
Nach einigen Recherchen fand ich einen Beitrag, in dem jemand vorschlug, die MTU-Größe zu überprüfen, was ich mit ping ip -f -l xxx tat. Tatsächlich hat -l 1473 die Nachricht Packet needs to be fragmented but DF set , also scheint 1472 die Grenze zu sein, an der Pakete nicht aufgeteilt werden und wo die Ausnahme nicht auftritt.
Um mein Ergebnis mit etwas anderem zu vergleichen, hüpfte ich auf ein System, auf dem es gut funktionierte, und führte diesen Befehl erneut aus - diesmal wurde kein Packet needs to be fragmented but DF set zurückgegeben. Ich habe sogar -l 65500 was das Maximum ist - keine Nachricht.
Es scheint tatsächlich so, als ob die MTU hier das Problem ist.

Ich werde die MTU des ersten Systems ändern, wo es nicht funktioniert und dies auf dem neuesten Stand halten.

  • Sieht so aus, als wären Jumbo-Frames die Lösung. Ich werde ein Update posten, sobald ich dies überprüft habe.
War diese Seite hilfreich?
0 / 5 - 0 Bewertungen