Runtime: Singleton HttpClient respektiert DNS-Änderungen nicht

Erstellt am 29. Aug. 2016  ·  77Kommentare  ·  Quelle: dotnet/runtime

Wie im folgenden Beitrag beschrieben: http://byterot.blogspot.co.uk/2016/07/singleton-httpclient-dns.html , sobald Sie eine freigegebene HttpClient Instanz behalten, um die Leistung zu verbessern, Es tritt ein Problem auf, bei dem der Client DNS-Eintragsaktualisierungen im Falle von Failover-Szenarien nicht berücksichtigt.

Das zugrunde liegende Problem ist, dass der Standardwert von ConnectionLeaseTimeout auf -1 , unendlich gesetzt ist. Es wird nur geschlossen, wenn der Client entsorgt wird, was sehr ineffizient ist.

Die Lösung besteht darin, den Wert auf dem Servicepunkt wie folgt zu aktualisieren:

var sp = ServicePointManager.FindServicePoint(new Uri("http://foo.bar/baz/123?a=ab"));
sp.ConnectionLeaseTimeout = 60*1000; // 1 minute

Leider ist dies heute mit .NET Core nicht möglich.

Entweder sollte ServicePointManager auf .NET Core übertragen werden oder ähnliche gleichwertige Funktionen sollten auf andere Weise aktiviert werden.

area-System.Net.Http enhancement

Hilfreichster Kommentar

Dies ist absolut ein Thema. Die von @darrelmiller vorgeschlagene Lösung scheint ein mangelndes Verständnis des Anwendungsfalls zu demonstrieren.

Wir verwenden Azure Traffic Manager, um den Zustand einer Instanz zu bewerten. Sie arbeiten auf DNS mit kurzen TTLs. Ein Host weiß nicht auf magische Weise, dass es ungesund ist, und beginnt mit der Ausgabe von C onnection:close- Headern. Der Traffic Manager erkennt einfach den fehlerhaften Zustand und passt die DNS-Auflösung entsprechend an. Vom Design her ist dies VOLLSTÄNDIG TRANSPARENT für Clients und Hosts ... so wie es aussieht, erhält eine Anwendung mit einem statischen HttpClient, der DNS (unwissentlich) durch einen Verkehrsmanager auflöst, nie einen neuen Host, wenn der zuvor aufgelöste Host fehlerhaft wird.

Ab netcore 1.1 suche ich derzeit nach einer Lösung für genau dieses Problem. Ich sehe keinen, jedenfalls nativ.

Alle 77 Kommentare

Ich glaube nicht, dass das dieselbe Eigenschaft ist. Das ist das Verbindungs-Timeout, nicht das ConnectionLeaseTimeout.

Wie in https://msdn.microsoft.com/en-us/library/system.net.servicepoint.connectionleasetimeout.aspx dokumentiert, sollte es in einigen Szenarien regelmäßig gelöscht werden. Es gibt jedoch keine offensichtliche Möglichkeit, das Verhalten in .NET Core zu ändern.

@onovotny Ja, das ist eine seltsame Sache, die eine explizite Verbindung herzustellen scheint

@darrelmiller , also das kommt aus meinem Bereich, ich behebt , wäre das ein großer Vorteil.

@onovotny , es gibt keine ServicePointManager Klasse in .NET Core. Das hört sich nach einem Problem mit .NET Framework (Desktop) an. Bitte öffnen Sie ein Problem in UserVoice oder Connect.

@onovotny Das ursprüngliche Problem, so wie ich es verstehe, besteht darin, dass auf der Serverseite jemand einen DNS-Eintrag aktualisieren möchte, damit alle zukünftigen Anforderungen an Host A an eine neue IP-Adresse gehen. Die beste Lösung ist meiner Meinung nach, wenn dieser DNS-Eintrag geändert wird, sollte die Anwendung auf Host A angewiesen werden, bei allen zukünftigen Antworten einen C onnection:close- Header an den Client zurückzugeben. Dadurch werden alle Clients gezwungen, die Verbindung zu trennen und eine neue aufzubauen. Die Lösung besteht darin, auf dem Client keinen Zeitüberschreitungswert zu haben, damit der Client eine C onnection:close sendet, wenn diese "Lease"-Zeitüberschreitung abläuft.

Dies passt gut zu der Frage in Stackoverflow Unit Testing DNS Failover unter Verwendung eines benutzerdefinierten DNS-Resolvers und kann weiteren Kontext zu diesem Problem geben, daher werde ich eine Querverknüpfung vornehmen (ich werde sie hier entfernen, wenn sie unangemessen ist).

Dies ist absolut ein Thema. Die von @darrelmiller vorgeschlagene Lösung scheint ein mangelndes Verständnis des Anwendungsfalls zu demonstrieren.

Wir verwenden Azure Traffic Manager, um den Zustand einer Instanz zu bewerten. Sie arbeiten auf DNS mit kurzen TTLs. Ein Host weiß nicht auf magische Weise, dass es ungesund ist, und beginnt mit der Ausgabe von C onnection:close- Headern. Der Traffic Manager erkennt einfach den fehlerhaften Zustand und passt die DNS-Auflösung entsprechend an. Vom Design her ist dies VOLLSTÄNDIG TRANSPARENT für Clients und Hosts ... so wie es aussieht, erhält eine Anwendung mit einem statischen HttpClient, der DNS (unwissentlich) durch einen Verkehrsmanager auflöst, nie einen neuen Host, wenn der zuvor aufgelöste Host fehlerhaft wird.

Ab netcore 1.1 suche ich derzeit nach einer Lösung für genau dieses Problem. Ich sehe keinen, jedenfalls nativ.

@kudoz83 Sie haben

Wenn dieser DNS-Eintrag geändert wird, sollte dies der Anwendung auf Host A mitgeteilt werden

Wenn Sie der Meinung sind, dass es nicht praktikabel ist, einen Ursprungsserver zu benachrichtigen, dass er c onnection:close- Header zurückgeben soll, dann bleiben Ihnen nur ein paar Möglichkeiten:

  • Schließen Sie regelmäßig Verbindungen von einem Client oder einer Middleware und zahlen Sie die Kosten für das erneute Öffnen dieser Verbindungen
  • Lassen Sie den Client einen Dienst abfragen, um zu wissen, wann er Verbindungen zurücksetzen soll.
  • Verwenden Sie eine Middleware, die intelligent genug ist, um zu erkennen, wann das DNS gewechselt wurde.

Und in Bezug auf mich, den Anwendungsfall nicht zu verstehen. Die ursprüngliche Ausgabe basierte auf einem Blogbeitrag, in dem der Anwendungsfall sich auf den Wechsel zwischen Produktions- und Staging-Slots bezog und nichts mit der Zustandsüberwachung zu tun hatte. Obwohl beide Szenarien davon profitieren könnten, Benachrichtigungen über DNS-/Slot-Switches empfangen zu können.

AFAIK gibt es keine Möglichkeit, einen ausgefallenen Host zu benachrichtigen, dass er ausgefallen ist? Im Allgemeinen würde dies bedeuten, dass es sich in einem Fehlerzustand befindet. Der ursprüngliche Beitrag spricht von Failover-Szenarien.

Azure Traffic Manager wird hier erklärt https://docs.microsoft.com/en-us/azure/traffic-manager/traffic-manager-monitoring

Es arbeitet auf DNS-Ebene und ist für Clients und Hosts transparent – ​​von Natur aus. Azure-Instanzen, die über Traffic Manager zur DNS-Auflösung miteinander kommunizieren, befinden sich in einer Krise, ohne dass sie eine TTL für die DNS-Aktualisierung für einen HttpClient festlegen können.

Die derzeitige Funktionsweise von HttpClient scheint im Widerspruch zu Microsofts eigenen Azure-Dienstangeboten zu stehen – und das ist der springende Punkt. Ganz zu schweigen davon, dass jeder ähnliche Dienst (zB Amazon Route 53) genau gleich funktioniert und genau dieselbe kurze DNS-TTL-Abhängigkeit hat.

Offensichtlich habe ich diesen Thread gefunden, weil ich von dem Problem betroffen bin. Ich versuche lediglich klarzustellen, dass es derzeit keine ideale Problemumgehung zu geben scheint, außer dass HttpClients willkürlich neu erstellt werden (auf Kosten der Leistung und Komplexität), um sicherzustellen, dass das DNS-Failover erfolgreich ist.

AFAIK gibt es keine Möglichkeit, einen ausgefallenen Host zu benachrichtigen, dass er ausgefallen ist?

Wenn ein "ausgefallener" Host keinen Code ausführen könnte, würden wir den HTTP-Statuscode 503 nicht benötigen, da ein Ursprungsserver niemals in der Lage wäre, ihn zurückzugeben. Es gibt Szenarien, in denen ein ausgefallener Host möglicherweise nicht in der Lage ist, eine Benachrichtigung zu verarbeiten, aber in diesen Fällen wird er wahrscheinlich auch eine aktive TCP/IP-Verbindung nicht sehr lange offen halten.

Als Ali dieses Thema ursprünglich auf Twitter ansprach, bevor er den Blog-Beitrag schrieb, war er sich einig, dass ein Client weiterhin Anfragen stellen und Antworten von einem ausgetauschten Server erhalten würde.

Ich verstehe, dass Ihre Situation anders ist und Sie sich nicht darauf verlassen können, dass ein Ursprungsserver eine Verbindung zuverlässig schließen kann. Ich bin mir nicht sicher, ob ich verstehe, warum Sie in Ihrer Situation keinen HttpMessageHandler verwenden können, um 5XX-Antworten zu erkennen, die Verbindung zu schließen und die Anforderung erneut zu versuchen. Oder noch einfacher: Überzeugen Sie einfach Ihren Webserver, „c onnection:close“ zurückzugeben, wenn er einen 5XX-Statuscode zurückgibt.

Es ist auch erwähnenswert, dass das betreffende Verhalten nicht in HttpClient auftritt. Es befindet sich im zugrunde liegenden HttpHandler oder sogar darunter, und es werden eine Reihe verschiedener Implementierungen von HttpHandler verwendet. Meiner Meinung nach stimmt das aktuelle Verhalten mit der Funktionsweise von HTTP überein. Ich stimme zu, dass es eine Herausforderung gibt, wenn DNS-Einstellungen aktualisiert werden, während Verbindungen geöffnet sind, aber dies ist kein .net-Problem. Dies ist ein HTTP-Architekturproblem. Das bedeutet nicht, dass .net nichts tun könnte, um dieses Problem zu verringern.

Ich bin gespannt, was Ihrer Meinung nach die Lösung sein sollte. Glauben Sie, dass so etwas wie ConnectionLeaseTimeout neu implementiert werden sollte, das einfach eine Verbindung regelmäßig trennt, unabhängig davon, ob ein Failover stattgefunden hat oder nicht? Oder habt ihr eine bessere Lösung?

Verzeihung,

Ich hätte nicht so kämpferisch rüberkommen sollen, wie ich gestern geklungen habe. Hatte einen schlechten Tag.

Ehrlich gesagt ist mir nicht bekannt, wie ConnectionLeaseTimeout unter der Decke funktioniert. Die gewünschte Funktionalität wäre, dass HttpClient konfigurierbar ist, um eine neue DNS-Suche durchzuführen, bevor neue Verbindungen hergestellt werden, anstatt die vorherige DNS-Suche zwischenzuspeichern, bis der Client verworfen wird. Ich glaube nicht, dass dies das Beenden einer bestehenden Verbindung erfordert ...

@kudoz83 Keine Sorge,

Soweit ich weiß, ist ConnectionLeaseTimeout buchstäblich ein Timer, der regelmäßig eine Leerlaufverbindung von der Clientseite schließt. Es ist in ServicePointManager implementiert, der im Kern nicht vorhanden ist. Dies unterscheidet sich vom Timeout "Verbindung im Leerlauf", bei dem eine Verbindung erst geschlossen wird, wenn sie über einen bestimmten Zeitraum nicht verwendet wurde.

Es gibt wahrscheinlich einen Win32-Aufruf, der das Leeren des DNS-Clientcaches ermöglichen würde. Das würde sicherstellen, dass neue Verbindungen eine DNS-Suche durchführen. Ich habe kurz danach gesucht, aber nicht gefunden, aber ich bin mir sicher, dass es irgendwo da ist.

In .net Core unter Windows wird die Verwaltung von HTTP-Verbindungen tatsächlich dem Betriebssystem überlassen. Am nächsten kommt der Aufruf der Win32-API WinHttpOpen https://github.com/dotnet/corefx/blob/master/src/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs# L718 Es gibt keine öffentlichen APIs, die ich finden kann, um irgendetwas mit dem _sessionHandle zu tun. Um die gewünschte Verhaltensänderung zu erreichen, müssten die OS-Leute Änderungen vornehmen. Zumindest verstehe ich das so.

Wäre es nicht einfacher, Azure Traffic Manager zu verwenden, um nur eine sehr niedrige TTL für die DNS-Einträge festzulegen? Wenn Sie sich keine Sorgen um langlebige Verbindungen machen, sollte eine niedrige TTL die Wahrscheinlichkeit minimieren, dass neue Verbindungen mit einem toten Server aufgebaut werden.

Der Vorteil von WinHttpHandler ist, dass wir mit dem Betriebssystem-Update Dinge wie HTTP/2 kostenlos erhalten. Der Nachteil ist, dass wir im verwalteten Code nicht viel Kontrolle haben.

@darrelmiller

[ConnectionLeaseTimeout] ist in ServicePointManager implementiert, der im Kern nicht vorhanden ist.

Es scheint, dass ServicePoint.ConnectionLeaseTimeout in .Net Standard 2.0/.Net Core 1.2 zurückkommt. (Obwohl ich keine Ahnung habe, ob zB WinHttpHandler-Verbindungen sich daran halten.)

Ich denke, hier gibt es einige Missverständnisse: DNS-TTLs haben nichts mit der Lebensdauer von HTTP/TCP-Verbindungen zu tun.

Neue HTTP-Verbindungen führen eine DNS-Suche durch und stellen eine Verbindung zur zurückgegebenen Hostadresse her. Mehrere HTTP-Anfragen können über dieselbe einzelne Verbindung gestellt werden, aber sobald die HTTP-Verbindung (und die zugrunde liegende TCP-Verbindung) geöffnet ist, führt sie keine DNS-Suche erneut durch.

Dies ist das richtige Verhalten. Selbst wenn Sie Tausende von Anfragen über eine tagelange Verbindung stellen, spielt die DNS-Auflösung keine Rolle, da sich die Verbindung nicht geändert hat. Wenn Sie über eine neue HTTP-Verbindung eine neue HTTP-Anfrage stellen, wird eine DNS-Suche für die neue Verbindung angezeigt.

Diese DNS-Aktualisierung hat nichts mit HttpClient zu tun, das standardmäßig nur HttpClientHandler (das selbst den HttpWebRequest Socket-Stack verwendet) und jede DNS-Auflösung wird von diesem Stack verarbeitet. ServicePointManager verwaltet ServicePoint Objekte in diesem Stack, die Verbindungen zu einem bestimmten Host sind. ServicePointManager hat einige minimale clientseitige Zwischenspeicherung von DNS-Lookups, die Sie optimieren können.

Standardeinstellungen sollen dafür sorgen, dass Verbindungen aus Effizienzgründen auf unbestimmte Zeit geöffnet bleiben. Wenn der verbundene Host nicht verfügbar ist, wird die Verbindung automatisch unterbrochen, was eine neue Verbindung mit einem neuen DNS-Lookup verursacht, jedoch werden DNS-TTLs keine neuen Verbindungen verursachen. Wenn Sie das benötigen, müssen Sie es in Ihre App codieren.

Wenn Sie möchten, dass eine weitere DNS-Suche durchgeführt wird, müssen Sie die Verbindung auf dem Client zurücksetzen. Sie können eine maximale Lebensdauer der TCP-Verbindung erzwingen, einen connection: close Header senden oder einfach die HttpClient oder die zugrunde liegenden ServicePoint entsorgen. Sie können die DNS-Auflösung auch mit Dns.GetHostAddresses und stattdessen die IP-Adresse für Ihre HTTP-Anfragen verwenden.

Ich hoffe, das hilft, wenn ich damit nicht weiterkomme, dann lass es mich wissen.

@manigandham

Ich denke, hier gibt es einige Missverständnisse: DNS-TTLs haben nichts mit der Lebensdauer von HTTP/TCP-Verbindungen zu tun.

Ja, DNS-TTLs sind ziemlich unabhängig, aber nicht vollständig.

Neue HTTP-Verbindungen führen eine DNS-Suche durch und stellen eine Verbindung zur zurückgegebenen Hostadresse her.

Ja, das ist richtig. Aber Windows hat einen lokalen DNS-Cache, der die TTL respektiert. Wenn die TTL hoch ist und der DNS-Server den DNS-Eintrag geändert hat, wird die neue Verbindung aufgrund des Client-Caching des DNS-Eintrags gegen die alte IP-Adresse geöffnet. Das Leeren des Client-DNS-Cache ist die einzige mir bekannte Problemumgehung.

Mehrere HTTP-Anfragen können über dieselbe einzelne Verbindung gestellt werden, aber sobald die HTTP-Verbindung (und die zugrunde liegende TCP-Verbindung) geöffnet ist, führt sie keine DNS-Suche erneut durch.

Nochmal richtig. In einem Produktions-/Staging-Swap, bei dem der ausgelagerte Server immer noch antwortet, führt dies dazu, dass viele zukünftige Anforderungen an den alten Server gehen, was schlecht ist. Das jüngste Gespräch betraf jedoch das Szenario, in dem ein Server ausfällt und der Traffic Manager auf einen neuen Server wechselt. Was mit der bestehenden Verbindung zum ausgefallenen Server passiert, ist unbekannt. Wenn es sich um einen schwerwiegenden Hardwarefehler handelt, besteht eine gute Chance, dass die Verbindung unterbrochen wird. Das würde den Client zwingen, eine Verbindung wiederherzustellen, und hier ist eine niedrige TTL sinnvoll. Wenn jedoch der Serverausfall den Server nicht unterbricht und er einfach 5XX-Antworten zurückgibt, wäre es für den Server nützlich, Connection:close zurückzugeben, um sicherzustellen, dass zukünftige Anforderungen an eine neue Verbindung gestellt werden, hoffentlich an die neue Funktion Server

Diese DNS-Aktualisierung hat nichts mit HttpClient zu tun, der standardmäßig nur HttpClientHandler verwendet (der selbst den HttpWebRequest-Socket-Stack verwendet).

Ja und nein. Auf .Net Core wurde der HttpClientHandler neu geschrieben und verwendet HttpWebRequest nicht mehr und verwendet daher keinen ServicePointManager. Daher der Grund für dieses Problem.

Ich habe IIS genauer unter die Lupe genommen und es scheint keine Möglichkeit zu geben, 5XX-Antworten mit den Headern " connection:close" zu erhalten . Es wäre notwendig, ein HttpModule zu implementieren, um diese Magie zu vollbringen.

Als FYI finden Sie hier einen verwandten Beitrag zu diesem Problem und eine Zwischenlösung. Vorsicht vor HttpClient

Entweder ServicePointManager sollte auf .NET Core übertragen werden

Obwohl wir ServicePointManager und HttpWebRequest für die Plattformparität eingeführt haben, unterstützt es die meisten Funktionen in .Net Core nicht, da der zugrunde liegende Stack entweder in WinHTTP oder Curl aufgelöst wird, die die erweiterten Steuerknöpfe nicht verfügbar machen.

Die Verwendung von ServicePointManager zum Steuern von HttpClient ist eigentlich ein Nebeneffekt bei der Verwendung von HttpWebRequest und dem verwalteten HTTP-Stack in .Net Framework. Da WinHttpHandler/CurlHandler auf .Net Core verwendet werden, hat SPM keine Kontrolle über HttpClient-Instanzen.

oder eine ähnliche äquivalente Funktionalität sollte auf andere Weise aktiviert werden.

Hier ist die Antwort des WinHTTP-Teams:

  1. Aus Sicherheitsgründen verwendet WinHTTP weiterhin die Ziel-IP, während der Client weiterhin Anfragen sendet und _es aktive Verbindungen gibt_. Es gibt keine Möglichkeit, die Zeit zu begrenzen, die diese Verbindungen aktiv bleiben.
  2. Wenn alle Verbindungen zum entfernten Endpunkt vom Server geschlossen wurden, werden _neue_ Verbindungen mit Abfrage des DNS und damit zur neuen IP hergestellt.

Um Clients auf die nächste IP zu zwingen, ist die einzige praktikable Lösung sicherzustellen, dass der Server neue Verbindungen ablehnt und bestehende schließt (in diesem Thread bereits erwähnt).
Dies ist das gleiche mit dem, was ich aus der ATM-Dokumentation verstehe: Die Erkennung erfolgt basierend auf GET-Anfragen, die 4 Mal fehlschlagen (nicht 200 Antworten). In diesem Fall werden die Verbindungen vom Server geschlossen.

Angesichts anderer Netzwerkgeräte, die selbst DNS zwischenspeichern (z. B. Heimrouter/APs), würde ich DNS-Failover nicht als Lösung für eine Technik mit geringer Latenz und hoher Verfügbarkeit empfehlen. Wenn dies erforderlich ist, würde ich für ein kontrolliertes Failover empfehlen, einen dedizierten LB (Hardware oder Software) zu verwenden, der weiß, wie der Abfluss ordnungsgemäß gestoppt wird.

So wie es aussieht, erhält eine Anwendung mit einem statischen HttpClient, der DNS (unwissentlich) durch einen Verkehrsmanager auflöst, nie einen neuen Host, wenn der zuvor aufgelöste Host fehlerhaft wird.

Wie oben erwähnt, wenn Sie mit ungesund meinen, dass der Server einen HTTP-Fehlercode zurückgibt und die TCP-Verbindung schließt, während Sie beobachten, dass die DNS-TTL abgelaufen ist, ist dies in der Tat ein Fehler.

Bitte beschreiben Sie Ihr Szenario genauer:

  1. Was sind der DNS-Cache und die TTLs des Client-Computers während des Failovers? Sie können ipconfig /showdns und nslookup -type=soa <domain_name> , um zu vergleichen, was die Maschine für die TTL hält und die TTLs des maßgeblichen NS.
  2. Gibt es aktive Verbindungen zum Remote-Server? Sie können netstat /a /n /b , um die Verbindungen und Prozesse anzuzeigen, die diese besitzen.
  3. Das Erstellen eines Repros würde uns sehr helfen: Bitte fügen Sie alle verfügbaren Informationen an, wie z.

Wenn Sie eine Verbindung zu einem Computer herstellen und die TCP/HTTP-Verbindung weiterhin funktioniert, warum sollten Sie dann irgendwann davon ausgehen, dass der Server nicht mehr geeignet ist.
Ich denke, es gibt Raum für Verbesserungen auf der Azure-Seite. Wenn beispielsweise Maschinen fehlerhaft sind oder Umgebungen wechseln, können vorhandene Verbindungen geschlossen werden.

oder entsorgen Sie einfach den ... zugrunde liegenden ServicePoint

@manigandham Wie? Es ist nicht wegwerfbar und die einzige relevante Methode, die ich sehen kann, ist CloseConnectionGroup die Sie nicht wirklich verwenden können, da der Gruppenname ein Implementierungsdetail ist (derzeit ein Hash des Handlers). Außerdem bin ich mir nicht sicher, was passieren würde, wenn Sie versuchen würden, den ServicePoint zu entsorgen, während HttpClient ihn verwendet (insbesondere gleichzeitig).

@ohadschn

.NET Core 2.0 bringt die ServicePoint Klassen und -Funktionen zurück: https://docs.microsoft.com/en-us/dotnet/api/system.net.servicepoint?view=netcore-2.0

Sie können zur Verwendung des Verbindungsleasing-Timeouts zurückkehren, um einen maximalen Zeitrahmen für aktive Verbindungen festzulegen, bevor sie zurückgesetzt werden.

Nichts Exotisches daran, die Verbindung zu schließen, während eine HttpClient Anfrage ausgeführt wird. Es wäre dasselbe, als würden Sie die Verbindung beim Surfen im Internet verlieren. Entweder schafft es die Anfrage nie oder Sie erhalten die Antwort nicht zurück - beides sollte zu HTTP-Fehlern führen, die Sie in Ihrer Anwendung behandeln müssen.

Mit der Einstellung Connection Lease Timeout können Sie sicherstellen, dass dies nur geschieht, nachdem eine Anfrage gesendet und vor der nächsten über das Zeitlimit zurückgesetzt wurde.

.NET Core 2.0 bringt die ServicePoint-Klassen und -Funktionen zurück: https://docs.microsoft.com/en-us/dotnet/api/system.net.servicepoint?view=netcore-2.0
Sie können zur Verwendung des Verbindungsleasing-Timeouts zurückkehren, um einen maximalen Zeitrahmen für aktive Verbindungen festzulegen, bevor sie zurückgesetzt werden.

Tatsächlich bringt es die Funktionalität nicht vollständig zurück. Die API-Oberfläche kam zurück. Aber es ist im Grunde eine No-Op, da die HTTP-Stacks dieses Objektmodell nicht verwenden.

cc: @stephentoub @geoffkizer

Ich würde viele der Teilnehmer in diesem Thread als eine Art "Traumteam" in diesem Thema bezeichnen, bin mir jedoch immer noch nicht sicher, ob ein Konsens darüber erzielt wurde, welche Best Practices wir heute haben. Es wäre großartig, von dieser Gruppe eine entscheidende Antwort auf einige wichtige Punkte zu erhalten.

Nehmen wir an, ich habe eine Allzweck-HTTP-Bibliothek, die auf viele Plattformen abzielt, die nicht alle ServicePointManager , und ich versuche, ein Verhalten bereitzustellen, das HttpClient Instanzen standardmäßig gemäß Best Practices intelligent verwaltet. Ich habe im Voraus keine Ahnung, wie Hosts heißen werden, geschweige denn, ob sie sich in Bezug auf DNS-Änderungen/C

  1. Wie verwalte ich HttpClient Instanzen optimal? Einer pro Endpunkt? Eine pro Host/Schema/Port? Buchstäblich ein Singleton insgesamt? (Ich bezweifle das, da Verbraucher möglicherweise Standardheader usw. nutzen möchten.)

  2. Wie würde ich am effektivsten mit diesem DNS-Problem umgehen? Instanzen nach 1 Minute entsorgen? Als "sehr unwahrscheinlich" behandeln und standardmäßig kein Entsorgungsverhalten bereitstellen?

(Übrigens keine hypothetische Situation; ich kämpfe gerade mit genau diesen Fragen.)

Danke!

Ich würde nicht einmal zu den Dream-Team-Tryouts eingeladen werden, aber ich habe eine Erkenntnis zu Ihrem Singleton-Kommentar HttpClient . Erstens können Sie immer die Methode SendAsync verwenden, um verschiedene Header und Adressen anzugeben, sodass Sie dies mit Ihrer Klasse einschließen können, die gemeinsame Daten ähnlich wie HttpClient (z. B. Standardheader, Basisadresse) bereitstellt ruft schließlich denselben einzelnen Client an. Aber noch besser, Sie können mehrere HttpClient Instanzen erstellen, die alle dasselbe HttpClientHandler teilen: https://github.com/dotnet/corefx/issues/5811.

Was das DNS-Problem angeht, scheint der Konsens hier zu sein:

Um Clients auf die nächste IP zu zwingen, ist die einzige praktikable Lösung sicherzustellen, dass der Server neue Verbindungen ablehnt und bestehende schließt.

Wenn dies für Sie nicht praktikabel ist, hat Darrel Miller hier weitere Optionen vorgeschlagen: https://github.com/dotnet/corefx/issues/11224#issuecomment -270498159.

@ohadschn Danke, und hoffentlich komme ich nicht zu sehr vom Thema ab, aber gibt es überhaupt einen _Vorteil_, denselben HttpClient (oder HttpClientHandler) zwischen mehreren Hosts zu teilen? Ich könnte mich irren, aber es scheint mir, dass ein neuer Host eine neue DNS-Suche, eine neue Verbindung, einen neuen Socket bedeutet. Kann ein Socket in TIME_WAIT zurückgefordert und für einen anderen Host verwendet werden? Ich hatte nicht angenommen, aber wir stoßen definitiv an die Grenzen meiner Expertise. Wenn ja, ist dies vielleicht der Vorteil der Verwendung eines HttpClient-Singletons sogar mit mehreren Hosts?

In Bezug auf die DNS-Beratung hatte ich das Gefühl, dass hier der Konsens am meisten fehlt, und das verständlicherweise. Es ist nicht möglich, sicherzustellen, dass sich der Server korrekt verhält, wenn Sie keine Kontrolle darüber haben, was an diesem Ende passiert. Ich _könnte_ etwas entwerfen, bei dem Instanzen regelmäßig verworfen werden. Die Frage ist, ob sich das in den meisten Fällen lohnt. Ich denke, es liegt größtenteils an mir, ein Urteil zu fällen. :)

Für mich ist der offensichtlichste Vorteil, das gleiche HttpClient [ Handler ] zu teilen, die Einfachheit. Sie müssen keine Dinge wie die Zuordnung zwischen Hosts und Clients beibehalten. Ich wüsste nichts von dem TIME_WAIT-Zeug, wirklich auch nicht mein Fachwissen.

In Bezug auf DNS haben Sie einen möglichen Ansatz erwähnt, bei dem Sie den Server nicht kontrollieren. Sie könnten auch einen Mechanismus implementieren, der das DNS regelmäßig abfragt und Instanzen nur dann verwirft, wenn eine tatsächliche Änderung aufgetreten ist...

Während die gegebenen Antworten, die angeben, was auf der Serverseite passieren soll, für eine ideale Welt korrekt sind, haben Entwickler in der realen Welt oft nicht die Kontrolle über den vollständigen serverseitigen Stack, mit dem sie kommunizieren. In dieser Hinsicht gefällt mir das Konzept eines ConnectionLeaseTimeout sehr gut, da es ein Zugeständnis an die Realitäten des täglichen Lebens von John und Jane Doeveloper ist.
Jemand schlug vor, Middleware zu schreiben, um ein solches Timeout auf .NET Core zu erzwingen. Würde diese auf NodaTime und System.Collections.Immutable basierende Klasse die Aufgabe erfüllen?

    public class ConnectionLeaseTimeoutHandler : DelegatingHandler
    {
        private static string GetConnectionKey(Uri requestUri) => $"{requestUri.Scheme}://{requestUri.Host}:{requestUri.Port}";

        private readonly IClock clock;
        private readonly Duration leaseTimeout;
        private ImmutableDictionary<string, Instant> connectionLeases = ImmutableDictionary<string, Instant>.Empty;

        public ConnectionLeaseTimeoutHandler(HttpMessageHandler innerHandler, IClock clock, Duration leaseTimeout)
            : base(innerHandler)
        {
            this.clock = clock;
            this.leaseTimeout = leaseTimeout;
        }

        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            string key = GetConnectionKey(request.RequestUri);
            Instant now = clock.GetCurrentInstant();

            Instant leaseStart = ImmutableInterlocked.GetOrAdd(ref connectionLeases, key, now);

            if (now - leaseStart > leaseTimeout)
            {
                request.Headers.ConnectionClose = true;
                ImmutableInterlocked.TryRemove(ref connectionLeases, key, out leaseStart);
            }

            return base.SendAsync(request, cancellationToken);
        }
    }

@snboisen Ich denke, das sieht richtig aus, aber halte mich nicht daran fest. :)

Für mich ist die größere Frage, ob das Senden eines Connection: close Headers überhaupt eine praktikable Lösung ist. Es lohnt sich hier zu wiederholen, was @darrelmiller in den Kommentaren des Blogbeitrags gesagt hat, der zu diesem Thread geführt hat:

ConnectionLeaseTimeout ist ein seltsames Tier, das mir nicht bewusst war, bis Oren es heute erwähnte. Dies führt dazu, dass ausgehende Anforderungen C

Ich respektiere Darrels Meinung und das lässt diesen ganzen Ansatz ziemlich hackig erscheinen. Auf der anderen Seite stimme ich zu, dass was soll ein Entwickler tun, der keine Kontrolle darüber hat, was auf der Serverseite passiert? Wenn es _manchmal_ funktioniert, ist es vielleicht die beste Option, die wir haben? Es ist sicherlich "billiger" als regelmäßig HttpClients zu entsorgen oder regelmäßig DSN-Suchen durchzuführen.

Ist ein Azure-Ticket für dieses Problem geöffnet?
Aus meiner Sicht sieht es so aus, als ob Sie alle versuchen, die Art und Weise zu umgehen, wie dies im Datenverkehrsmanager von Azure implementiert ist.
Ich bin kein Azure-Benutzer, daher betrachte ich es aus der Ferne und fühle mich weniger geneigt, durch seine Reifen zu springen.

@tmds Ich habe noch keinen gesehen, aber wie andere erwähnt haben, betrifft dies mehr als nur Azure Traffic Manager: AWS hat ein ähnliches Konzept und es gibt Azure App Service mit seinem Staging-/Produktions-Slot-Swapping. Und ich wette, andere Cloud-Anbieter bieten auch so etwas Ähnliches wie diesen Austauschmechanismus an.

@snboisen Sagen Sie, dass AWS und andere Cloud-Anbieter (welche?) auch DNS-Timeouts verwenden, um diese Funktionen zu implementieren?

@tmds Für AWS ja, zumindest für Failover: https://aws.amazon.com/route53/faqs/#adjust_ttl_to_use_failover
Ich kenne andere Cloud-Anbieter nicht, aber es scheint wahrscheinlich, dass einige dies auch tun.

@tmds Für AWS ja, zumindest für Failover: https://aws.amazon.com/route53/faqs/#adjust_ttl_to_use_failover
Ich kenne andere Cloud-Anbieter nicht, aber es scheint wahrscheinlich, dass einige dies auch tun.

Hier gibt es zwei problematische Szenarien:

  • Client ist mit einem Server verbunden, der nicht mehr vorhanden ist
  • Client ist mit einem Server verbunden, auf dem jetzt eine veraltete Version der Software läuft

DNS-Failover hängt mit dem ersten zusammen. Es ist angebracht, nicht erreichbare Server aus dem DNS zu entfernen (in AWS, Azure, ...).
Der richtige Weg, dies beim Client zu erkennen, ist ein Timeout-Mechanismus. Ich bin mir nicht sicher, ob es eine solche Eigenschaft auf HttpClient gibt (maximale Antwortzeitüberschreitung bei einer hergestellten Verbindung).
Auf Protokollebene definieren die neueren Websocket- und http2-Protokolle Ping-/Pong-Nachrichten, um die Verbindungsfähigkeit zu überprüfen.

Wenn ich vorschlage, ein Problem mit Azure zu öffnen, dann für das zweite Szenario.
Es gibt keinen Grund, warum ein Client eine funktionierende Verbindung schließen sollte. Wenn der Server nicht mehr geeignet ist, sollten Clients getrennt werden und der Server muss unerreichbar gemacht werden.
Ich habe ein schönes Dokument zu AWS Blue/Green-Bereitstellungen gefunden: https://d0.awsstatic.com/whitepapers/AWS_Blue_Green_Deployments.pdf.
Es listet mehrere Techniken auf. Für diejenigen, die DNS verwenden, wird _DNS TTL-Komplexitäten_ erwähnt. Für diejenigen, die einen Load Balancer verwenden, _keine DNS-Komplexitäten_.

@tmds Danke für den Link, das ist sehr interessant.

Ich frage mich, wie häufig es ist, sich auf DNS-TTL im Vergleich zu einem Load Balancer zu verlassen. Letzteres scheint im Allgemeinen eine viel zuverlässigere Lösung zu sein.

Ähnliches Verhalten beim Arbeiten mit 2 verschiedenen API-Apps in Azure App Services, die über HttpClient kommunizierten. Wir haben in unseren Lasttests etwa 250 Anfragen bemerkt, wir bekamen Zeitüberschreitungen und "Eine Verbindung konnte nicht hergestellt werden" HttpRequestException - Es spielte keine Rolle, ob es sich um gleichzeitige Benutzer oder sequentielle Anfragen handelte.

Als wir zu einem using Block gewechselt haben, um sicherzustellen, dass HttpClient in jeder Anfrage verworfen wird, haben wir keine dieser Ausnahmen erhalten. Der Leistungseinbruch, falls vorhanden, war von einem Singleton bis zur using Syntax vernachlässigbar.

Als wir zu einem using Block gewechselt haben, um sicherzustellen, dass HttpClient in jeder Anfrage verworfen wird, haben wir keine dieser Ausnahmen erhalten. Der Leistungseinbruch, falls vorhanden, war von einem Singleton bis zur using Syntax vernachlässigbar.

Es geht nicht unbedingt um Leistung, es geht darum, keine TCP-Verbindungen zu verlieren und SocketException s zu bekommen.

Von MSDN :

HttpClient soll einmal instanziiert und während der gesamten Lebensdauer einer Anwendung wiederverwendet werden. Vor allem in Serveranwendungen wird das Erstellen einer neuen HttpClient Instanz für jede Anfrage die Anzahl der verfügbaren Sockets unter hoher Last erschöpfen. Dies führt zu SocketException Fehlern.

Siehe auch https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

@khellang Danke für den Link (und die Klarstellung zu den SocketException s, da hatte ich definitiv ein Missverständnis) - Ich bin auf diesen Beitrag und 3 oder 4 andere Beiträge gestoßen, die genau dasselbe beschreiben.

Mir ist bewusst, dass eine einzelne Instanziierung beabsichtigt ist, aber wenn wir sie in Azure App Services verwenden, erreichen wir unter einer ziemlich bescheidenen Last (wie oben erwähnt 250 sequenzielle Anforderungen) durchweg HttpRequestExceptions . Sofern es keine andere Lösung gibt, ist die Beseitigung von HttpClient die Problemumgehung, die wir implementieren mussten, um diese konsistenten Ausnahmen zu vermeiden.

Ich gehe davon aus einer „richtigen“ Lösung wäre, diese Ausnahmen zu fangen, brechen Sie alle anstehenden Anforderungen explizit das Dispose HttpClient , Re-instantiate die HttpClient , und alle Anforderungen wiederholen.

Scheint ein bisschen durcheinander für etwas zu sein, das

soll einmal instanziiert und während der gesamten Lebensdauer der Anwendung wiederverwendet werden

Wir würden uns über eine sauberere Lösung freuen, aber es scheint, als ob unser Code nicht stimmt, sondern eher eine Einschränkung des Frameworks. Ich bin mehr als glücklich, falsch zu liegen, ich brauche nur eine Anleitung, wie wir dies wie beabsichtigt verwenden können und diese Probleme nicht auftreten.

@snboisen Ich habe Ihren ConnectionLeaseTimeoutHandler getestet und festgestellt, dass er mit mehreren Verbindungen nicht funktioniert. Jedes Mal, wenn das Lease-Timeout auftritt, wird nur an eine Verbindung ein "Schließen" ausgegeben, aber beim parallelen Senden von Anforderungen können mehrere Verbindungen mit einer einzelnen HttpClient-Instanz geöffnet sein. Nach dem, was ich in Fiddler sehe (ich verwende eine schlechte IP-Adresse, um Fehler zu generieren, um den DNS-Switch zu überprüfen), verwenden einige Anfragen nach dem Senden von "Schließen" die neue IP und einige Anfragen verwenden die alte IP. In meinem Fall habe ich ein Verbindungslimit von 2 und es dauert mehrere Iterationen des Verbindungsleasing-Timeouts, um beide Verbindungen zu aktualisieren. Bei mehr gleichzeitigen Verbindungen von einem Produktionsserver wäre es natürlich viel schlimmer, aber es würde wahrscheinlich nach einiger Zeit umschalten.

Ich kenne keine Möglichkeit, "close" für jede einzelne Verbindung auszugeben, die von einer HttpClient-Instanz verwendet wird. Das wäre sicherlich ideal, aber ich werde eine GetOrCreate()-Factory-Methode für HttpClient verwenden und alle 120 Sekunden eine neue HttpClient-Instanz erstellen, um sicherzustellen, dass alle neuen Verbindungen erstellt werden. Ich bin mir nur nicht sicher, wie ich die alte HttpClient-Instanz entsorgen soll, sobald alle Anfragen zum Schließen der Verbindungen abgeschlossen sind, aber ich denke, .NET kümmert sich darum, weil es nie ein Problem war, eine neue HttpClient-Instanz pro Anfrage zu erstellen.

Als Randnotiz glaube ich, dass die Aktualisierung auf dem Client erfolgen muss. Der Server kann weder für die Übermittlung von DNS-Änderungen noch für Middleware verantwortlich sein. Der Zweck von DNS besteht darin, diese Verantwortung auszugleichen, sodass Sie IPs jederzeit ohne Koordination zwischen einem Client und einem Server ändern können und nicht nur einen Anzeigenamen für eine IP angeben. Der perfekte Anwendungsfall ist eine AWS Elastic IP-Adresse. Die Änderung wird nur im DNS widergespiegelt. AWS wird nicht wissen, dass Sie "close"-Header von Ihrem Webserver senden müssen, der auf einer ec2-Instance ausgeführt wird. AWS erkennt nicht jeden Webserver, der auf einer von ihnen verwalteten ec2-Instanz sitzt. Das Szenario für den alten Server, der keinen "close"-Header sendet, sind neue Serverbereitstellungen, bei denen genau dieselbe Software auf einem neuen Server bereitgestellt wird, wobei der gesamte Datenverkehr über DNS umgeschaltet wird. Auf dem alten Server sollte sich nichts ändern, insbesondere wenn Sie zurückwechseln müssen. Mit DNS sollte alles reibungslos an einem Ort geändert werden.

Unabhängig von den hier besprochenen größeren Problemen, ist ConnectionLeaseTimeout jetzt im Kern implementiert? Und wenn ja, warum wird dieses Thema nicht geschlossen? Ich war in der Lage, ein Beispiel genau wie im ursprünglichen Beitrag ohne Probleme zu codieren. Ob das meine Verbindungs-DNS-Probleme löst oder nicht, es _scheint_ ConnectionLeaseTimeout _ist_ im Kern implementiert.

ServicePointMananager.ConnectionLeaseTimeout ist in .NET Core nicht implementiert. Die Verwendung dieser Eigenschaft in .NET Core ist ein No-Op. Es ist nur in .NET Framework funktionsfähig. Und standardmäßig ist diese Eigenschaft in .NET Framework auf "off" gesetzt.

Mir ist bewusst, dass eine einzelne Instanziierung beabsichtigt ist, aber wenn wir sie in Azure App Services verwenden, treffen wir ständig HttpRequestExceptions unter einer ziemlich bescheidenen Last (oben erwähnt, 250 sequenzielle Anforderungen). Sofern es keine andere Lösung gibt, ist das Verwerfen des HttpClient die Problemumgehung, die wir implementieren mussten, um diese konsistenten Ausnahmen zu vermeiden.

Es ist uns gelungen, das Problem der Socket-Erschöpfung zu beheben, indem wir HttpClient in eine benutzerdefinierte API einschließen, die es jeder http-Anfrage in einer Anwendung ermöglicht, dieselbe HttpClient Instanz wiederzuverwenden (es tut mehr als nur dies , wie RestSharp aber das ist auch einer der Vorteile). Dies abzurufen ist hinter einem lock{} weil wir alle 60 Minuten ein neues HttpClient erstellen und dieses stattdessen zurückgeben. Dies hat es uns ermöglicht, umfangreiche HTTP-Anfragen von unserer Webanwendung (aufgrund der starken Integrationen, die wir haben) durchzuführen, ohne alte Verbindungen, die nicht geschlossen wurden, wiederzuverwenden. Wir machen dies jetzt seit ungefähr einem Jahr mit Erfolg (nachdem wir die Probleme mit der Sockelerschöpfung hatten).

Ich bin mir sicher, dass dies immer noch Auswirkungen auf die Leistung hat (insbesondere aufgrund der vielen Sperren), aber es ist besser als alles, was aufgrund von Socket-Erschöpfung stirbt.

@KallDrexx warum müssen Sie die 'globale' Instanz sperren? Naiv würde ich es statisch lagern und alle 60 Minuten durch ein neues ersetzen. Wäre das nicht genug?

Unser Code, um ein HttpClient lautet:

```c#
var timeSinceCreated = DateTime.UtcNow - _lastCreatedAt;
if (timeSinceCreated.TotalSeconds > SecondsToRecreateClient)
{
Schloss (Vorhängeschloss)
{
timeSinceCreated = DateTime.UtcNow - _lastCreatedAt;
if (timeSinceCreated.TotalSeconds > SecondsToRecreateClient)
{
_currentClient = new HttpClient();
_lastCreatedAt = DateTime.UtcNow;
}
}
}

        return _currentClient;

```

Ich vermute also, dass die Sperre nur einmal pro Minute auftritt, damit nicht mehrere Threads gleichzeitig versuchen, ein neues HttpApiClient zu erstellen. Ich dachte, wir sperren aggressiver.

Ich verstehe, also ist es ein Bequemlichkeitsgrund.
Alternativ können Sie entweder ein Timer-basiertes (asynchrones) Update verwenden oder zulassen, dass mehrere davon parallel erstellt werden (Thread-unsicherer lazy-Initialisierungsstil) oder einen anderen Synchronisationsmechanismus verwenden und die anderen Threads während der Erstellungszeit nicht blockieren (sie könnte das alte wiederverwenden).

Ich verstehe, also ist es ein Bequemlichkeitsgrund.

Ich weiß nicht, ob ich das als Komfort einstufen würde.

Wenn zu viele Threads gleichzeitig versuchen, ein neues HttpClient zu erstellen, riskieren wir immer noch eine Socket-Erschöpfung, weil jede Minute zu viele erstellt werden, insbesondere wenn die alten Sockets in dieser einen Minute nicht richtig bereinigt wurden (verursacht eine Kaskade).

Außerdem bräuchte ich immer noch ein gewisses Maß an Sperren, da ich nicht weiß, ob das Aktualisieren von DateTime eine atomare Operation ist (ich vermute nicht), und daher können einige Threads den _lastCreatedAt in . lesen mitten in einem Update-Vorgang.

Und damit alle Threads den alten Client wiederverwenden, während gleichzeitig ein Thread einen neuen Client erstellt, ist eine Menge komplexer Logik erforderlich, sodass wir garantiert sind, dass ein Thread den neuen Client erfolgreich erstellt. Ganz zu schweigen davon, dass wir immer noch Sperren brauchen, weil wir nicht garantieren können, dass ein Thread _currentClient nicht zurückgibt, während ein anderer Thread ihn instanziiert.

Es gibt viel zu viele Unbekannte und zusätzliche Komplexität, um dies in der Produktion zu riskieren.

@KallDrexx

  • Es scheint, dass ReaderWriterLockSlim für Ihre Verwendung besser geeignet wäre.
  • DateTime Zuweisung sollte atomar sein, wenn Sie echtes x64 ausführen (da es ein einzelnes ulong Feld enthält). Beachten Sie, dass Ihr aktueller Code nicht threadsicher ist, wenn dies nicht der Fall ist, da Sie den DateTime Wert außerhalb der Sperre beobachten.
  • Der von @karelz vorgeschlagene Timer-basierte Ansatz erfordert überhaupt kein DateTime ... der Timer würde einfach HttpClient hochschalten . Sie müssten das Feld HttpClient mit einer Speicherbarriere umgeben ( volatile , Interlocked.Exchange usw.).

Es scheint, dass ReaderWriterLockSlim besser für Ihre Verwendung geeignet wäre.

Ordentlich hatte ich nichts von dieser Klasse gewusst! Sie haben dies jedoch erwähnt, was mich auch dazu gebracht hat, darüber nachzudenken, auch SemaphoreSlim , da dies async/await-freundlich ist (wo es scheint, dass ReaderWriterLockSlim nicht ist), was bei möglichen Threadkonflikten helfen kann, wenn ein neues HttpClient ist erforderlich. Andererseits wird das ReaderWriterLockSlim das potenzielle DateTime nicht-atomare Problem, auf das Sie richtig hingewiesen haben, besser handhaben, also muss ich mir darüber Gedanken machen, danke!

Der von @karelz vorgeschlagene Timer-basierte Ansatz erfordert überhaupt keine DateTime ... der Timer würde einfach den HttpClient umschalten. Sie müssten das HttpClient-Feld jedoch immer noch mit einer Speicherbarriere umgeben (flüchtig, Interlocked.Exchange usw.).

Ah, ich hatte falsch verstanden, was er meinte. Das macht Sinn, obwohl ich mich mit den Speicherbarrieren-Mechanismen besser vertraut machen muss, um es richtig zu machen.

Danke.

Bearbeiten: Nur um hinzuzufügen, dies ist ein guter Grund, warum ich denke, dass etwas eingebaut werden sollte, um dies zu handhaben, damit jeder Verbraucher von HttpClient dies nicht erneut implementieren / durchlaufen muss.

Ich frage mich, ob es immer noch ein Problem mit .net Core 2.0 ist? Müssen wir uns noch um die DNS-Änderung kümmern?

Ich frage mich, ob es immer noch ein Problem mit .net Core 2.0 ist? Müssen wir uns noch um die DNS-Änderung kümmern?

Mein Verständnis ist, dass dies kein Problem mit DNS selbst ist, nur dass HttpClient versucht, vorhandene Verbindungen wiederzuverwenden (um die Erschöpfung des Sockets zu verhindern), und daher verbinden Sie sich auch bei DNS-Änderungen immer noch mit der alten Server, weil Sie über die vorherige Verbindung gehen.

@KallDrexx , bedeutet dies, dass es immer noch Probleme mit der Blau-Grün-Bereitstellung geben wird?

@innerhalb eines Jahres nach meinem Verständnis ja. Wenn Sie ein HttpClient erstellen und es gegen Production Green verwenden, dann die Produktion zu Blue wechseln, hat das HttpClient immer noch eine offene Verbindung zu Green, bis die Verbindung geschlossen wird.

Ich weiß, dass dies alt ist, aber ich glaube, Sie sollten DateTime.UtcNow nicht für die Zeitintervallmessung verwenden. Verwenden Sie eine monotone Zeitquelle wie Stopwatch.GetTimestamp. Auf diese Weise sind Sie immun gegen die Anpassung der Ortszeit durch den Operator oder die Zeitsynchronisation. Es gab einige fehlerhafte Chipsätze, die dazu führten, dass die QPC-Werte zurückgingen, aber ich glaube nicht, dass viele von ihnen jetzt verwendet werden.

var sp = ServicePointManager.FindServicePoint(new Uri("http://foo.bar/baz/123?a=ab"));
sp.ConnectionLeaseTimeout = 60*1000; // 1 Minute

Dieser Code wird im Konstruktor platziert?

@whynotme8998 @lundcm Ich glaube nicht. Wenn Sie HttpClient Singleton sind und Sie auf mehrere Endpunkte zugreifen, ist der Konstruktor nicht der richtige Ort, um ihn zu platzieren.
Überprüfen Sie diese Implementierung . Auch in der README des Projekts gibt es einen Link zu einem Blogbeitrag, der das Problem erklärt. Dieser Artikel verweist auch auf diesen Thread.

Kann mir jemand helfen, genau zu verstehen, was ConnectionLeaseTimeout bedeutet?

Wenn ich in meiner App 1 Minute einstelle, bedeutet das, dass die Verbindungslease jede Minute abläuft? Was genau bedeutet das für das Gespräch mit meinem Backend?

Ich sehe, dass die Leute über die DNS-Aktualisierung im Back-End sprechen? Würde das Veröffentlichen meines ASP.NET-Back-Ends in einer standardmäßigen Azure-Web-App eine DNS-Aktualisierung verursachen?

Würde ein Timeout von 1 Minute nicht möglicherweise ein Fenster hinterlassen, in dem das DNS aktualisiert wurde, das Timeout jedoch noch nicht aufgetreten ist?

Bearbeiten. Zu erkennen, dass dies in .NET Standard 2.0 nicht funktioniert. Worum geht es bei der Arbeit?

Warten Sie darauf https://www.stevejgordon.co.uk/introduction-to-httpclientfactory-aspnetcore oder implementieren Sie Teile davon selbst.

Das Ändern von ConnectionLeaseTimeout funktionierte auf Xamarin mit .netstandard 2.0 nicht !!!

@paradisehuman Ich nehme an, du meinst ServicePoint.ConnectionLeaseTimeout . AFAIK funktioniert es auch nicht auf .NET Core. In .NET Core 2.1 haben wir SocketsHttpHandler.PooledConnectionLifetime . Auch das neue HttpClientFactory in ASP.NET kann beides nutzen und tut noch mehr für Sie.
Mono/Xamarin verfügt über eine eigene Networking-Stack-Implementierung (sie verbrauchen keinen CoreFX-Quellcode für das Networking) – daher ist es am besten, den Fehler mit weiteren Details in ihrem Repository anzugeben.

Leider ist dies heute mit .NET Core nicht möglich. Entweder sollte ServicePointManager auf .NET Core übertragen werden oder ähnliche gleichwertige Funktionen sollten auf andere Weise aktiviert werden.

@onovotny , SocketsHttpHandler in .NET Core 2.1 macht PooledConnectionLifetime verfügbar, das einem ähnlichen Zweck wie ConnectionLeaseTimeout dient, nur auf Handlerebene. Können wir dieses Thema an dieser Stelle als angesprochen betrachten?

@stephentoub sehe das gerade jetzt, ja, ich denke, PooledConnectionLifetime sollte es ansprechen. Die Community kann ein weiteres Problem eröffnen, wenn noch etwas benötigt wird.

@onovotny @karelz @stephentoub ,

Würden Sie alle zustimmen, dass dies eine genaue Zusammenfassung ist, wie das ursprüngliche Singleton-HttpClient/DNS-Problem am besten gelöst werden kann?

  • Verwenden Sie in .NET Framework 2.0+ ServicePoint.ConnectionLeaseTimeout .

  • Verwenden Sie in .NET Core 2.1 SocketsHttpHandler.PooledConnectionLifetime .

  • Für alle anderen Plattformen/Ziele/Versionen gibt es keine zuverlässige Lösung/Hack/Workaround, also versuchen Sie es nicht einmal.

Während ich weiterhin Dinge aus diesem und anderen Threads lerne (z. B. dass ConnectionLeaseTimeout in .NET Standard 2.0 nicht zuverlässig plattformübergreifend funktioniert), bin ich wieder am Zeichenbrett und versuche, dies auf eine Art und Weise aus einer Bibliothek zu lösen das auf so vielen Plattformen/Versionen wie möglich funktioniert. Plattform-Sniffing ist in Ordnung. Es ist in Ordnung, HttpClient regelmäßig zu entsorgen / neu zu erstellen, wenn dies effektiv ist. Ich bin mir ziemlich sicher, dass ich bei meinem ersten Versuch, der darin bestand, einfach in regelmäßigen Abständen einen connection:close Header an den Host zu senden, einen Fehler gemacht habe. Danke im Voraus für jeden Rat!

@tmenier Für alle anderen Plattformen/Ziele/Versionen gibt es keine zuverlässige Lösung/Hack/

Sie können die Instanz jederzeit regelmäßig recyceln (zB einfach eine neue Instanz erstellen und sie auf eine statische Variable setzen, die andere verwenden).
Das macht HttpClientFactory . Sie können stattdessen HttpClientFactory verwenden.

@karelz Danke, ich denke, das bietet mir einen Weg nach vorne. Vielleicht sollte ich das Plattform-Sniffing einfach überspringen und Instanzen überall wiederverwenden, oder ist es ratsam, die anderen Ansätze zu verwenden, sofern verfügbar (um Overhead zu vermeiden usw.)?

Das Knifflige könnte sein, zu wissen, _wann_ es sicher ist, recycelte Instanzen zu entsorgen (ich muss mir Sorgen um Parallelität/ausstehende Anfragen machen), aber ich kann einen Blick darauf werfen, wie HttpClientFactory umgeht. Danke noch einmal.

@tmenier Sie möchten nicht bei jedem HttpClient Aufruf Instanzen recyceln, da dies zu einer Erschöpfung des TCP-Sockets führt . Das Recycling muss verwaltet werden (Zeitüberschreitung oder Pflege eines Pools mit HttpClient s, die ablaufen können.

@KallDrexx Richtig, ich rede nur davon, _periodisch_ einen Singleton (oder "Pseudo" -Singleton) zu recyceln, z. B. alle paar Minuten. Ich glaube, es wird die Verwaltung eines Pools und eine Art verzögerte Entsorgung toter Instanzen beinhalten, sobald festgestellt wird, dass keine ausstehenden Anfragen vorliegen.

Entschuldigung, dass du falsch verstanden hast, was du mit recyceln meinst.

Alte sollten Sie nicht entsorgen müssen. Lassen Sie GC sie sammeln.
Der Code sollte so einfach sein wie das periodische Setzen eines neuen "Pseudo"-Singletons basierend auf dem Timer. Nichts mehr.

Ich habe festgestellt, dass die Nichtbeseitigung zu Verlangsamungen führen kann, da Ihnen in Hochleistungsszenarien die HTTP-Verbindungen ausgehen.

@tmenier Informationen zur Verwendung von SocketsHttpHandler.PooledConnectionLifetime in In .NET Core 2.1 ist die Erstellung von HttpClientHandler vollständig gekapselt in https://github.com/dotnet/wcf/blob/master/src/System.Private.ServiceModel/src/System/ ServiceModel/Channels/HttpChannelFactory.cs. Gibt es eine Möglichkeit, dies bei Verwendung des WCF-Clients zu tun?

@edavedian Ich würde vorschlagen, im dotnet/wcf-repo zu fragen. cc @mconnew @Lxiamail

der zugrunde liegende Stack wird entweder in WinHTTP oder Curl aufgelöst, wodurch die erweiterten Steuerknöpfe nicht angezeigt werden

Gibt es in .Net Core einen HTTP-Client, der über Sockets funktioniert?

Gibt es in .Net Core einen HTTP-Client, der über Sockets funktioniert?

Ja, SocketsHttpHandler, und ab 2.1 ist dies die Standardeinstellung.

Cachet HttpClient URIs, die es trifft, ähnlich wie RestClient meiner Meinung nach tut? Wenn dies der Fall ist, teilen Sie mir bitte mit, auf welcher Version (.NET Core 3.0/.NET Framework 3.0 usw.) dies unterstützt wird.

Ich sehe, dass das Caching nur in RestClient wenn das Projekt eine .Net Core 2.1+ App ist?

Cachet HttpClient die URIs, die es trifft, ähnlich wie RestClient meiner Meinung nach tut?

HttpClient führt kein Caching durch.

@SpiritBob es ist nicht klar, auf welche Form des Cachings Sie sich beziehen. Dieses Problem ist geschlossen; Wenn Sie bitte eine neue Ausgabe mit Fragen öffnen, können wir Ihnen helfen, Antworten zu finden.

@scalablecory Ich glaube, Davidsh hat mit dem reagiert, was ich mir vorgestellt hatte. Danke!

Habe gerade diesen Thread gelesen und es scheint, als ob es einen Anwendungsfall gab, der nicht angesprochen wurde.

Wenn HttpClientFactory wird, schließen die resultierenden HttpClient s automatisch Verbindungen und führen eine neue DNS-Suche durch, wenn sie ein HttpRequestException mit einem inneren SocketException , das anzeigt, dass die Host nicht mehr gültig ist und sich das DNS möglicherweise geändert hat (Fehlercode 11001 - kein solcher Host bekannt)?

Wenn nicht, was ist die Problemumgehung, um HttpClient , DNS-Lookups durchzuführen, bevor die TTL für DNS-Einträge abläuft?

Ich verwende derzeit HttpClientFactory mit einem benutzerdefinierten HttpMessageHandler (konfiguriert über Dependency Injection mit AddHttpClient<T, U>().ConfigurePrimaryHttpMessageHandler(...) ) und wenn sich ein DNS-Eintrag ändert, bevor die TTL abläuft, wird ein Fehler ausgegeben. Ich sehe dies insbesondere, wenn ich eine Reihe von Uploads in Azure Blob Storage durchführe.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen