Unter welchen Bedingungen (falls vorhanden) empfiehlt es sich, zwei Server abzufragen und nur die schnellste Antwort zu verwenden?

12

Ich habe gefragt, was nun eine von der Community gelöschte Frage zu SO ist, warum jemand Javascript verwenden sollte Promise.race, und ein hochrangiger Benutzer hat dies kommentiert:

Wenn Sie zwei Dienste haben, die einen Wert berechnen, können Sie beide parallel abfragen und den jeweils zuerst zurückgegebenen Wert verwenden, anstatt einen abzufragen, auf einen Fehler zu warten und dann den zweiten abzufragen.

Ich habe über Redundanz und diesen Anwendungsfall im Allgemeinen gegoogelt, aber ich konnte nichts finden, und aus meiner Sicht ist es nie eine gute Idee, einem Server / Dienst einfach eine Arbeitslast hinzuzufügen, wenn Sie die Antwort nicht verwenden.

Adelin
quelle
Spielzeugbeispiel: Anstatt immer Quicksort zu verwenden, kopieren Sie die Daten, senden sie an eine Quicksort, eine Mergesort und eine Heapsort usw. Sie müssen die Eingabe nicht überprüfen, um festzustellen, ob es sich um einen pathalogischen Fall handelt für jeden von ihnen, denn es wird kein pathalogischer Fall für alle von ihnen sein
Caleth
Das Papier von Dean und Barroso, The Tail at Scale, nennt eine Variation dieses Ansatzes "Hedged Requests". Es werden auch die Vor- und Nachteile mehrerer verwandter Ansätze zur Steuerung der Long-Tail-Variabilität in Bezug auf Fehlerraten und Latenz erörtert.
Daniel Pryden
Die zweite "Serveranfrage" könnte eine Fälschung sein. Es könnte nur 5 Sekunden dauern und dann eine Platzhalterantwort zurückgeben. Das verschafft Ihnen eine Auszeit bei der eigentlichen Anfrage.
user253751
Bestellen Sie eine Lyft und dann eine Uber. Nimm, was zuerst kommt.
user2023861
@ user2023861 Während ein Fahrer sinnlos auf Ihren Standort zufuhr, hätte er / sie stattdessen eine andere Anfrage
annehmen können

Antworten:

11

Ich würde argumentieren, dass dies eher eine ökonomische Frage ist. Dies ist jedoch ein Urteil, zu dem Ingenieure fähig sein sollten. Daher antworte ich.

Ich teile meine Antwort in vier Teile:

  • Risikomanagement
  • Strategien
  • Kosten
  • Intuition

Risikomanagement

Daher kann es vorkommen, dass Ihr Client keine Antwort vom Server erhält. Ich gehe davon aus, dass dies nicht auf einen Programmfehler zurückzuführen ist (ansonsten besteht die Lösung darin, ihn zu beheben, also mach das). Stattdessen muss es sich um eine zufällige Situation handeln, auf die Sie keinen Einfluss haben ...

Aber nicht jenseits Ihres Wissens. Du musst wissen:

  • Wie oft passiert es?
  • Welche Auswirkungen hat es?

Wenn beispielsweise ein Fehler auftritt und nur in etwa 2% der Fälle ein erneuter Versuch unternommen wird, lohnt es sich wahrscheinlich nicht, ihn zu beheben. Wenn es ungefähr 80% der Zeit passiert, hängt es davon ab,

Wie viel Zeit muss der Kunde warten? Und wie sich das in Kosten niederschlägt ... Sie haben eine kleine Verzögerung bei einer regulären Bewerbung, es ist wahrscheinlich keine große Sache. Wenn es wichtig ist und Sie über eine Echtzeitanwendung oder ein Online-Videospiel verfügen, werden Benutzer abgelehnt, und Sie sollten wahrscheinlich besser in mehr oder bessere Server investieren. Andernfalls können Sie wahrscheinlich eine Meldung "Laden" oder "Warten auf Server" einfügen. Wenn die Verzögerung nicht wirklich groß ist (in der Größenordnung von zehn Sekunden), kann sie selbst für die reguläre Anwendung zu groß sein.


Strategien

Wie ich oben sagte, gibt es mehr als einen Weg, um dieses Problem zu lösen. Ich gehe davon aus, dass Sie bereits die Try-Fail-Retry-Schleife implementiert haben. Also, mal sehen ...

  • Lade Nachricht. Es ist billig und hilft bei der Kundenbindung.
  • Abfrage parallel. Kann schneller sein, kann immer noch scheitern. Benötigt einen redundanten Server (kann teuer sein), wird Serverzeit und Netzwerkverkehr verschwenden.
  • Fragen Sie parallel ab, um den schnelleren Server einzurichten, und verwenden Sie diesen von da an. Kann schneller sein, kann immer noch scheitern. Benötigt redundante Server (kann teuer sein), wird nicht so viel Serverzeit und Netzwerkverkehr verschwenden.

Jetzt merke ich, dass diese immer noch scheitern können. Wenn wir davon ausgehen, dass eine Abfrage an einen Server eine Ausfallwahrscheinlichkeit von 80% hat, hat eine parallele Abfrage an zwei Server eine Ausfallwahrscheinlichkeit von 64%. Daher müssen Sie es möglicherweise noch einmal versuchen.

Ein zusätzlicher Vorteil der Auswahl des schnelleren Servers und seiner weiteren Verwendung besteht darin, dass die Wahrscheinlichkeit eines Ausfalls des schnelleren Servers aufgrund von Netzwerkproblemen geringer ist.

Was mich daran erinnert, wenn Sie herausfinden können, warum die Anfrage fehlschlägt, tun Sie dies. Es kann Ihnen helfen, die Situation besser zu meistern, auch wenn Sie die Ausfälle nicht verhindern können. Benötigen Sie beispielsweise mehr Übertragungsgeschwindigkeit auf der Serverseite?

Etwas mehr:

  • Stellen Sie weltweit mehrere Server bereit und wählen Sie den Server nach Standort aus.
  • Führen Sie einen Lastenausgleich auf der Serverseite durch (ein dedizierter Computer nimmt alle Anforderungen auf und leitet sie an Ihre Server weiter. Sie können dort Ihre Parallelität oder eine bessere Ausgleichsstrategie festlegen).

Und wer hat gesagt, dass Sie nur eines davon tun müssen? Sie können eine Lademeldung einfügen, mehrere Server abfragen, die über den gesamten Wrold verteilt sind, um die schnelleren zu ermitteln, und von da an bei einem erneuten Fehlversuch in einer Schleife nur diese Server verwenden. Jeder dieser Server kann ein Cluster von Computern mit Lastenausgleich sein . Warum nicht? Na ja, kostet ...


Kosten

Es gibt vier Kosten:

  • Die Entwicklungskosten (in der Regel sehr billig)
  • Die Bereitstellungskosten (normalerweise hoch)
  • Die Kostenlaufzeit (abhängig von der Art der Anwendung und dem Geschäftsmodell)
  • Die Kosten des Scheiterns (wahrscheinlich niedrig, aber nicht notwendig)

Sie müssen sie ausbalancieren.

Nehmen wir zum Beispiel an, Sie verdienen ungefähr einen Dollar pro zufriedenen Benutzer. Das haben Sie 3000 Benutzer pro Tag. Dass die Anfragen in etwa 50% der Fälle fehlschlagen. Und dass 2% der Nutzer ohne Bezahlung abreisen, wenn die Anfrage fehlschlägt. Dies bedeutet, dass Sie 30 Dollar pro Tag verlieren (3000 * 50% * 2%). Angenommen, die Entwicklung der neuen Funktion kostet 100 US-Dollar, und die Bereitstellung der Server kostet 800 US-Dollar. Wenn Sie die Laufzeitkosten außer Acht lassen, bedeutet dies, dass sich die Investition in ((100 + 800) / 30 rentiert ) 30 Tage. Jetzt können Sie Ihr Budget überprüfen und entscheiden.

Betrachten Sie diese Werte nicht als repräsentativ für die Realität, ich habe sie aus mathematischen Gründen ausgewählt.

Nachträge:

  • Denken Sie daran, dass ich auch die Details ignoriere. Möglicherweise haben Sie nur geringe Bereitstellungskosten, zahlen jedoch für die CPU-Zeit und müssen dies berücksichtigen.
  • Einige Kunden wissen es zu schätzen, wenn Sie ihr Datenpaket nicht in redundanten Anforderungen verschwenden.
  • Die Verbesserung Ihres Produkts kann dazu beitragen, natürliche Werbung zu bringen.
  • Opportunitätskosten nicht vergessen. Sollten Sie etwas anderes entwickeln?

Die Sache ist, dass Sie, wenn Sie das Problem in Bezug auf die Ausgleichskosten betrachten, eine Schätzung der Kosten für die Strategien vornehmen können, die Sie in Betracht ziehen, und diese Analyse verwenden, um zu entscheiden.


Intuition

Intuition wird durch Erfahrung gefördert. Ich schlage nicht vor, diese Art von Analyse jedes Mal durchzuführen. Einige Leute tun es, und das ist in Ordnung. Ich schlage vor, dass Sie dies verstehen und eine Intuition dafür entwickeln.

Darüber hinaus lernen wir im Ingenieurwesen neben dem Wissen, das wir aus der tatsächlichen Wissenschaft erhalten, auch in der Praxis und erstellen Richtlinien darüber, was funktioniert und was nicht. Daher ist es oft ratsam zu sehen, wie der Stand der Technik ist ... obwohl Sie manchmal außerhalb Ihrer Region sehen müssen.

In diesem Fall würde ich mir Online-Videospiele ansehen. Sie haben Ladebildschirme, sie haben mehrere Server, sie wählen einen Server basierend auf der Latenz und sie können dem Benutzer sogar erlauben, Server zu wechseln. Wir wissen, dass das funktioniert.

Ich würde vorschlagen, dies zu tun, anstatt bei jeder Anforderung Netzwerkverkehr und Serverzeit zu verschwenden. Beachten Sie auch, dass selbst bei redundanten Servern Fehler auftreten können.

Theraot
quelle
2
Ich glaube nicht, dass ich es sagen muss, aber das ist eine großartige Antwort :) Ich wusste, dass ich es bei den ersten 10 Zeilen akzeptieren werde, aber ich gab dir die Möglichkeit, immer noch zu scheitern und es bis zum Ende zu lesen. Sie haben nicht
Adelin
9

Dies ist akzeptabel, wenn die Zeit des Clients wertvoller ist als die Zeit auf dem Server.

Wenn der Kunde schnell und genau sein muss. Sie können die Abfrage mehrerer Server rechtfertigen. Und es ist schön, die Anfrage abzubrechen, wenn eine gültige Antwort eingeht.

Und natürlich ist es immer ratsam, die Eigentümer / Manager der Server zu konsultieren.

Toon Krijthe
quelle
Warum Sie müssen den Antrag stornieren? Das ist doch subjektiv.
17.
@ JᴀʏMᴇᴇ, Das ist in Paranoia gebaut. Ich habe einmal mit einem System gearbeitet, das seine Warteschlange nicht geleert hat, und es ist abgestürzt, als die Warteschlange voll war (Ja, es war professionelle Software).
Toon Krijthe
4

Diese Technik kann die Latenz reduzieren. Die Antwortzeit des Servers ist nicht deterministisch. In der Größenordnung ist es wahrscheinlich, dass mindestens ein Server schlechte Antwortzeiten aufweist. Jeder, der diesen Server verwendet, hat daher auch schlechte Antwortzeiten. Durch das Senden an mehrere Server wird das Risiko verringert, mit einem Server mit schlechter Leistung zu kommunizieren.

Die Kosten umfassen zusätzlichen Netzwerkverkehr, verschwendete Serververarbeitung und Anwendungskomplexität (obwohl dies in einer Bibliothek verborgen sein kann). Diese Kosten können reduziert werden, indem nicht verwendete Anforderungen storniert werden oder kurz gewartet wird, bevor eine zweite Anforderung gesendet wird.

Hier ist ein Papier , und ein anderer . Ich erinnere mich, dass ich in einem Google-Artikel auch deren Implementierung gelesen habe.

Michael Green
quelle
2

Ich stimme den anderen Antworten größtenteils zu, denke aber, dass dies in der Praxis äußerst selten sein sollte. Ich wollte ein viel häufigeres und vernünftigeres Beispiel dafür geben, wann Sie es verwenden würden Promise.race(), etwas, das ich vor ein paar Wochen zufällig verwendet habe (nun, Pythons Äquivalent).

Angenommen, Sie haben eine lange Liste von Aufgaben, von denen einige parallel ausgeführt werden können und einige vor anderen ausgeführt werden müssen. Sie können alle Aufgaben ohne Abhängigkeiten starten und dann mit auf diese Liste warten Promise.race(). Sobald die erste Aufgabe abgeschlossen ist, können Sie alle Aufgaben starten, die von dieser ersten Aufgabe und Promise.race()erneut von der neuen Liste in Kombination mit den noch nicht abgeschlossenen Aufgaben aus der ursprünglichen Liste abhängen . Wiederholen Sie dies, bis alle Aufgaben erledigt sind.

Hinweis: Die API von Javascript ist dafür nicht ideal geeignet. Es ist so ziemlich das Nötigste, was funktioniert, und Sie müssen einiges an Klebercode hinzufügen. Mein Punkt ist jedoch, dass Funktionen wie race()selten für Redundanz verwendet werden. Sie sind in erster Linie dazu da, wenn Sie die Ergebnisse aus allen Versprechungen erhalten möchten, aber nicht darauf warten möchten, dass sie alle abgeschlossen sind, bevor Sie weitere Maßnahmen ergreifen.

Karl Bielefeldt
quelle
Das Problem ist, dass Sie, zumindest mit Javascript's Promise.race, die Aufgabe jedes Mal starten, wenn Sie die Race-Methode ausführen. Es wird sich nicht um die unvollendete Aufgabe handeln, sondern um eine Reihe neuer Aufgaben, ohne Rücksicht darauf, was zuvor ausgeführt wurde (es sei denn, Sie implementieren diese Logik auf Aufgabenebene). Ansonsten wird die ursprüngliche Liste vergessen und es bleibt nur der Rückgabewert der ersten Aufgabe
Adelin
1
Versprechungen in Javascript werden eifrig gestartet, wenn sie new Promiseaufgerufen werden, und nicht neu gestartet, wenn sie Promise.race()aufgerufen werden. Einige Implementierungen von Versprechungen sind faul, aber Eifer ist weitaus häufiger. Sie können dies testen, indem Sie in der Konsole ein Versprechen erstellen, das sich bei der Konsole anmeldet. Sie werden sehen, dass es sofort protokolliert wird. Dann geben Sie dieses Versprechen an Promise.race(). Sie werden sehen, es wird nicht erneut protokolliert.
Karl Bielefeldt
Ah das ist wahr. Aber afaik der Rückgabewert des Restes der Versprechen außer dem ersten wird vergessen, mit Versprechen.race
Adelin
Deshalb habe ich gesagt, dass die API nicht ideal ist. Sie müssen den ursprünglichen Aufgabensatz in einer Variablen irgendwo ablegen.
Karl Bielefeldt
1

Zusätzlich zu den technischen Überlegungen können Sie diesen Ansatz verwenden, wenn er Teil Ihres tatsächlichen Geschäftsmodells ist.

Variationen dieses Ansatzes sind bei Echtzeitgeboten für Anzeigen relativ häufig . In diesem Modell fordert ein Publisher (Werbeflächenanbieter) Werbetreibende (Werbeanbieter) auf, auf eine Impression eines bestimmten Nutzers zu bieten . So wird für jeden solchen Eindruck, würden Sie jede der Werbetreibende abfragen abonniert hat , zu einem Endpunkt eine Abfrage mit dem Eindruck Details Senden von jedem Werbetreibenden zur Verfügung gestellt (oder alternativ ein Skript durch die Werbenden bereitgestellt als ein Endpunkt auf Ihrem eigenen Server ausgeführt wird ), Rennen alle diese Anfragen bis zu einem Timeout (zB 100ms) und dann das höchste Gebot nehmen, die anderen ignorieren.

Eine besondere Variante, die die Wartezeit des Kunden verkürzt, besteht darin, dass der Publisher einen Mindestzielwert für das Gebot zulässt, sodass das erste Gebot des Werbetreibenden, das diesen Wert übersteigt, sofort akzeptiert wird (oder falls keines der Gebote übersteigt) der Wert, der maximale wird genommen). In dieser Variante könnte also die erste ankommende Abfrage gewinnen und die andere verworfen werden, selbst wenn sie genauso gut oder sogar noch besser sind.

yoniLavi
quelle