2016-06-30 3 views
4

Ich habe diesen Code (die unwichtigen Details sind, dass er auf EC2-Instanzen in AWS ausgeführt wird und Nachrichten in einer SQS-Warteschlange verarbeitet).Sollte ich eine asynchrone Operation ausführen, wenn die Methode bereits asynchron ist

Die erste Anweisung in der Methode ruft einige Daten über HTTP ab, die zweite Anweisung speichert den Status in einem lokalen Dynamodatenspeicher.

public bool HandleMessage(OrderAcceptedMessage message) 
{ 
    var order = _orderHttpClient.GetById(message.OrderId); 
    _localDynamoRepo.SaveAcceptedOrder(message, order); 
    return true; 
} 

Die Leistungsmerkmale sind, dass die HTTP-Rundreise 100-200 Millisekunden, und der dynamo-Schreib dauert etwa 10 Millisekunden dauert.

Beide dieser Operationen haben async Versionen. Wir könnten es wie folgt schreiben:

public async Task<bool> HandleMessage(OrderAcceptedMessage message) 
{ 
    var order = await _orderHttpClient.GetByIdAsync(message.OrderId); 
    await _localDynamoRepo.SaveAcceptedOrderAsync(message, order); 
    return true; 
} 

So ist die Führung ist, dass seit der ersten Operation „länger als 50 Millisekunden dauern könnte auszuführen“ es soll Gebrauch von async und wartet machen. (1)

Aber was ist mit der zweiten, schnellen Operation? Welches dieser beiden Argumente ist richtig:

Machen Sie es nicht async: Es erfüllt nicht die 50ms-Kriterium und es ist nicht der Overhead wert.

Machen Sie es async: Der Aufwand wurde bereits von der vorherigen Operation bezahlt. Es gibt bereits aufgabenbasierte Asynchronität, und es lohnt sich, sie zu verwenden.

1) http://blog.stephencleary.com/2013/04/ui-guidelines-for-async.html

Antwort

5

die unwichtigen Details sind, dass sie auf EC2-Instanzen in AWS laufen, Verarbeiten von Nachrichten auf einer SQS-Warteschlange

Eigentlich, denke ich, das ist ein wichtiges Detail. Weil dies keine UI-Anwendung ist; es ist eine Serveranwendung.

die Führung ist, dass seit der ersten Operation „länger als 50 Millisekunden dauern könnte auszuführen“

Diese Anleitung nur für UI-Anwendungen gilt. Somit ist die 50ms-Richtlinie hier bedeutungslos.

Welche dieser beiden Argumente korrekt ist

Asynchrony geht es nicht um Geschwindigkeit. Es geht darum, Threads freizugeben. Bei der 50ms-Richtlinie für UI-Apps geht es darum, den UI-Thread freizugeben. Auf der Server-Seite async geht es darum, Thread-Pool-Threads freizugeben.

Die Frage ist, wie viel Skalierbarkeit Sie wollen/brauchen? Wenn Ihr Backend skalierbar ist, dann empfehle ich generell async, weil das Thread-Pool-Threads freigibt. Dadurch wird Ihre Web-App skalierbarer und kann schneller auf Änderungen der Auslastung reagieren. Aber das bringt nur Vorteile, wenn Ihr Backend mit Ihrer Web-App skaliert werden kann.

+0

Danke für die Antwort. Ja, das ist eine Server-App, die in der Spitze eine hohe Auslastung und Nebenläufigkeit aufweist. Gibt es eine äquivalente Leitzahl von ms zum "Freigeben von Thread-Pool-Threads" oder ist es "immer"? – Anthony

+1

@Anthony: Wenn Ihre App stark ausgelastet ist und Ihr Backend skalieren kann, würde ich einfach "immer" sagen. Ich kenne keine Metriken. In diesem Fall können Sie mit all 'async' Ihren Thread-Pool maximal nutzen. –

1

erster Hinweis, dass die größt Kosten für async in Web-Apps ist die Verringerung der Produktivität. Das ist es, worüber wir die Vorteile abwägen. Sie müssen darüber nachdenken, wie viel Code infiziert wird, wenn Sie diese Methode asynchron ausführen.

Der Vorteil ist das Speichern eines Threads für die Dauer des Anrufs. Ein 200-ms-HTTP-Anruf ist ein ziemlich guter Fall für Async (obwohl es nicht sicher ist, da es auch davon abhängt, wie oft Sie den Anruf ausführen).

Das 50ms-Kriterium ist keine harte Nummer. Diese Empfehlung gilt für Echtzeit-UI-Apps.

Eine nützlichere Nummer ist latency times frequency. Das sagt Ihnen, wie viele Threads im langfristigen Durchschnitt verbraucht werden. Seltene Anrufe müssen nicht optimiert werden.

100 Dynamo Anrufe pro Sekunde bei 10ms kommen heraus bei einem Thread blockiert. Das ist nichts. Das ist wahrscheinlich kein guter Kandidat für Async.

Natürlich, wenn Sie den ersten Anruf asynchron machen, können Sie den zweiten async auch fast keine inkrementellen Produktivitätskosten machen, weil alles bereits infiziert ist.

Sie können die Nummern selbst ausführen und basierend darauf entscheiden.

1

Dies könnte zu einer rechthaberischen Diskussion führen ... aber lass es uns versuchen. tl; dr: ja, halten Sie es async.

Sie in einer Bibliothek sind und Sie kümmern sich nicht um die Synchronisationskontext, so sollten Sie es nicht erfassen und Ihren Code ändern in:

var order = await _orderHttpClient.GetByIdAsync(message.OrderId).ConfigureAwait(false); 
await _localDynamoRepo.SaveAcceptedOrderAsync(message, order).ConfigureAwait(false); 

Übrigens: nach dem ersten await ed Anruf, Sie werde wahrscheinlich auf einem Thread des Thread-Pools landen. Selbst wenn Sie die nicht asynchrone Version SaveAcceptedOrder() verwenden, wird sie nicht blockiert. Dies ist jedoch nichts, auf das Sie sich verlassen sollten und Sie kennen nicht unbedingt den Typ der asynchronen Methode (CPU gebunden oder IO gebunden = "async by design"). Wenn es IO-gebunden ist, ist es nicht notwendig, es in einem Thread auszuführen.

+0

Ich bin nicht völlig klar, was das 'ConfigureAwait' Standardverhalten ist - Was ist die Einstellung, wenn es nicht angegeben ist? – Anthony

+2

Der Standardwert ist 'true'. Sehen Sie hier den Abschnitt "Configure Context" für weitere Details https://msdn.microsoft.com/en-us/magazine/jj991977.aspx – Krumelur

1

Wenn Sie eine beliebige Remote-Aufruf machen, machen Sie es asynchron. Ja, DynamoDB-Aufrufe sind schnell (außer wenn ein untergeordneter Hash-Schlüssel oder viele Gigabyte Daten in einer einzigen Tabelle vorhanden sind), aber Sie machen sie immer noch über das Internet (auch wenn Sie in AWS sind EC2 etc), und so sollten Sie keines der Eight Fallacies of Distributed Computing ignorieren - und vor allem nicht 1) Das Netzwerk ist zuverlässig oder 2) Latenz ist Null.