Quartznet: Async/await-Unterstützung

Erstellt am 30. März 2015  ·  27Kommentare  ·  Quelle: quartznet/quartznet

Es wäre großartig, wenn Quartz.net asynchrone Jobs unterstützt.

Außerdem habe ich den entsprechenden Thread aus der Mailingliste gefunden: http://quartz.10975.n7.nabble.com/quartznet-3668-using-Tasks-in-quartz-jobs-td10753.html

Alle 27 Kommentare

Danke für die Anfrage, wir können die Möglichkeiten mit Release 3.0 untersuchen.

Ich unterstütze das - es wäre großartig, wenn Quartz async/await unterstützen würde.

Haben wir eine Vorstellung von einem Zeitplan für die Veröffentlichung von 3.0?

Ich habe mit der Überholung von async/await begonnen und es ist ein ziemliches Unterfangen. Ich werde keinen Zeitplan nennen, um Sie nicht zu enttäuschen, aber ich werde Sie auf dem Laufenden halten. Es wird sicher Alpha- und Beta-Versionen zum Testen geben.

Ich habe einige massive Änderungen an APIs und Interna vorgenommen, um async/await mit Commit https://github.com/quartznet/quartznet/commit/f78cf38e3f3ed6b76f044b9b0aa78e66dc6c5b62 besser zu unterstützen. Dies sollte eine bessere interne Effizienz beim Betrieb der Quartz-Infrastruktur bieten.

Die eigentlichen Jobs bleiben weiterhin problematisch. Wenn wir möchten, dass Jobs asynchrone Aufgaben unterstützen, würde dies auch bedeuten, die aktuelle Thread-Pool-Nutzung aufzugeben. Worker-Threads können nicht korrekt neu eingegeben werden, wenn await verwendet wird - dies würde die Garantie verletzen, dass maximal so viele Jobs ausgeführt werden, wie Thread-Pool-Threads ausgeführt werden.

Async/await auf Auftragsebene zu unterstützen, würde bedeuten, sich auf den .NET-Thread-Pool und eine größere Trennung des Ausführungsflusses zu verlassen. Ich zögere ein bisschen, diese Änderung vorzunehmen..

Können Sie ein alternatives Muster vorschlagen, damit ich erwartungsfähige Operationen innerhalb von Jobs aufrufen kann, wenn diese Arbeit nicht durchgeführt wird?

Um auf den Punkt zu kommen, das Versprechen zu brechen, nicht mehr Jobs zu brechen, als es Threads im Thread-Pool gibt: Ist das Brechen dieses Versprechens nicht eines der Argumente dafür, diese Arbeit überhaupt zu machen? Wenn viele E/A-Vorgänge stattfinden, möchten wir keine Benutzermodus-Threads binden, die auf die E/A-Vervollständigung warten, da dies bedeutet, dass wir viele präventive Planung von Threads für die Abfrage der E/A-Vervollständigung und -Erschöpfung haben Thread-Limits, obwohl sie keine sinnvolle CPU-Arbeit leisten. Wenn wir also die CPU- und I/O-Auslastung maximieren wollen, warum nicht mehr Jobs als die maximale Threadpool-Anzahl zulassen, wenn dies bedeutet, dass wir viele dieser Jobs auf I/O-Überlastung warten lassen können und keinen Thread versuchen.

@mleyb Ich habe noch nicht alle Hoffnung aufgegeben und fummele immer noch an Implementierungsalternativen herum ...

@Jawvig Das Mischen des Quartz-Thread-Pools und des .NET CLR-Thread-Pools hat einige Konsequenzen, die möglicherweise schwer zu kommunizieren sind.

Wir könnten effektiv zulassen, dass Tausende von Jobs gleichzeitig gestartet werden, wenn wir eine Thread-Pool-Größe von 10 haben. Alle Jobs starten und starten asynchrone Operationen, sodass der Worker-Thread zurückgegeben wird und neue Jobs gestartet werden können.

Möglicherweise benötigen wir einen speziellen Thread-Pool (basierend auf TaskScheduler) für asynchrone Arbeit, der die Arbeit auslöst, aber dann wird es interessant. Dies führt mich zu der Annahme, dass dieser spezielle Thread-Pool keine Größenkonfiguration zulassen sollte, da "es darauf ankommt".

Ich würde sagen, dass dies bis zu dem Punkt, an dem die Erschöpfung des CLR-Threadpools auftritt, in Ordnung sein könnte, sagen wir, dass es sich um eine ASP.NET-App handelt, bei der Anforderungen in die Warteschlange gestellt werden. Quartz konnte die Erschöpfung umgehen, indem es über einen eigenen Thread-Pool verfügte und Synchronisierungsjobs verwendete.

Ich wollte also nur diese Punkte ansprechen, die problematisch sein könnten, und es ist großartig, dass diese Diskussion stattfindet. Ich möchte gespannt alle Meinungen über Vor- und Nachteile zu diesem Thema hören.

Einige Denkanstöße:

  • Sollten wir eine IAsyncJob-Schnittstelle haben, IInteruptableJob zurückziehen und IJobExcutionContext CancellationToken enthalten, das sowohl von asynchronen als auch von synchronen Jobs verwendet werden kann?
  • Sollten wir IAsyncJobs nur auf einem Planer ausführen lassen, der den CLR-Thread-Pool als einzigen Thread-Pool konfiguriert hat – oder sollten wir Hybrid ausführen, bei dem Synchronisierungsjobs auf Thread-Pool und Async-Jobs zusätzlich ausgeführt werden?

Re: Einstellung IInterruptableJob : Auch wenn wir über eine Hauptversion sprechen, denke ich, dass wir IInterruptableJob aus Gründen der Abwärtskompatibilität beibehalten sollten. Ich kann mir nicht vorstellen, unter welchen Umständen das Abbrechen über CancellationToken und auch das Anrufen Interrupt ein Problem darstellen würde. Quartz ist leicht aufgebläht, aber wir könnten das Codedokument ändern, um darauf hinzuweisen, dass IInterruptableJob in zukünftigen Versionen möglicherweise entfernt wird und dass die Verwendung von CancellationToken in IJobExecutionContext bevorzugt wird.

Betreff: IAsyncJob Schnittstelle: Ich denke, das ist das Richtige.

Betreff: Ausführen IAsyncJob s auf einem Scheduler mit hybriden Threadpools: Ich habe den Eindruck, dass dies eine schlechte Idee wäre. Wenn man sich auf die CLR verlässt, um einen optimierten ThreadPool bereitzustellen, auf dem TPL seine Operationen ausführen kann, erscheint es falsch, die Annahmen zu untergraben, auf denen sie aufgebaut wurde, indem eine Reihe zusätzlicher Threads in demselben Prozess sitzen, für den sie keine Sichtbarkeit hat . Um es klar zu sagen, ich wäre dafür, darauf zu bestehen, dass nur IAsyncJob s auf einem Planer ausgeführt werden können, in dem der CLR-Thread-Pool als einziger Thread-Pool konfiguriert ist.

Ich habe jetzt die erste Iteration mit dem Konzept durchgeführt, Beispiel 16 enthält Code zur Einführung des IAsyncJob:

https://github.com/quartznet/quartznet/tree/quartznet-3/src/Quartz.Examples/example16

Wäre IJobAsync nicht die richtige Namenskonvention, die mit anderen asynchronen Musternamen übereinstimmen würde?

Am 26. Juli 2015 um 6:23 Uhr schrieb Marko Lahma [email protected] :

Ich habe jetzt die erste Iteration mit dem Konzept durchgeführt, Beispiel 16 enthält Code zur Einführung des IAsyncJob:

https://github.com/quartznet/quartznet/tree/quartznet-3/src/Quartz.Examples/example16


Antworten Sie direkt auf diese E-Mail oder zeigen Sie sie auf GitHub an.

Ich glaube, dass das gemeinsame Muster mit async _methods_ darin besteht, Methodennamen mit Async zu postfixieren (was Quartz derzeit nicht tut). Ich würde also argumentieren, dass Klassennamen wie AsyncJob eine bessere Botschaft über die Absicht haben.

@lahma

ASP.NET app that has requests queuing up. Quartz has been able to circumvent exhaustion by having its own thread pool and using sync jobs.

Wenn ich mich nicht irre, sprichst du von "Gegendruck". Ich bin auf dieses Problem gestoßen, bei dem asynchrone Aufgaben von einem sehr schnellen und praktisch unendlich langen Nachrichtenproduzenten in die Warteschlange gestellt wurden, der innerhalb einer Minute so viele Nachrichten in die Warteschlange gestellt hat, dass dem Prozess der Arbeitsspeicher ausgeht.

Ich habe damals Reactive Extensions verwendet, und das Ersetzen durch Tpls Dataflow hat das Problem perfekt gelöst. Wie wäre es mit ActionBlock:
https://msdn.microsoft.com/en-us/library/hh194684%28v=vs.110%29.aspx

var executor = new ActionBlock<IJobAsync>(async job => await job.ExecuteAsync(), 
    new ExecutionDataflowBlockOptions{
         MaxDegreeOfParallelism = maxTasksLimit,
         BoundedCapacity = 1
    });

 // scheduler
SchduleJob(job) {
     await executor.SendAsync(job)
  1. Wir können die gleichzeitige Ausführung von Aufgaben begrenzen.
  2. Die Pufferkapazität des Executors ist begrenzt, sodass der Absender "wartet", wenn das Limit erreicht ist.

@vchekan danke! Ich muss das noch ein bisschen studieren.

ActionBlock ist ziemlich praktisch. Ich verwende es, um die Drosselung gegen eine API eines Drittanbieters zu implementieren.

Um auf Vadims Kommentar aufzubauen, müssten wir wahrscheinlich mehr als await executor.SendAsync(job) tun, um Fehlzündungsstrategien usw. zu implementieren. Wir könnten Task.Wait(TimeSpan) und das als Fehlzündung behandeln, wenn das Timeout abläuft.

Wie dem auch sei, abgesehen davon wollte ich die API-Methodennamen in dem von Ihnen angeführten Beispiel kommentieren, Marko. Wie Sie angemerkt haben, sind die meisten asynchronen Methoden, denen Sie begegnen, mit dem Suffix „Async“ versehen, während dies in Ihrem Beispiel nicht der Fall war. Außerdem werden viele Methoden asynchron geworden sein, ohne den Namen geändert zu haben, zB IScheduler.Start , IScheduler.Interrupt . Ich denke, dies könnte zu viele unbeabsichtigte Folgen für den vorhandenen Code beim Upgrade haben. Da es sich um void -Methoden handelt, kompiliert der Client-Code immer noch dagegen und segelt durch das Erstellen von Aufgaben, ohne auf deren Abschluss zu warten.

Darf ich vorschlagen, die vorhandenen Methoden beizubehalten und neue Methoden hinzuzufügen, die mit „Async“ enden? Die vorhandenen Methoden können als synchrone Methoden implementiert werden, die die Async-Methoden und .Wait() für ihre Aufgaben aufrufen. Ich habe das Gefühl, dass dies einen viel einfacheren Upgrade-Pfad für Menschen ohne unbeabsichtigte Nebenwirkungen bieten wird.

Entschuldigung, wenn ich hier etwas Offensichtliches über die Absicht vermisse!

@Jawvig Ich bin immer noch ein bisschen auf dem Zaun, was die Verwendung von Async Postfix angeht. Ich sehe nicht viel Sinn darin, sowohl synchrone als auch asynchrone Versionen von Methoden zu haben, da asynchron jetzt bevorzugt wird. Eine nur asynchrone öffentliche API zu haben, wirft dann die Frage auf, ob es sinnvoll ist, Async-Postfix zu haben. Das Brechen der Kompilierung ist natürlich schön, damit der API-Verbraucher weiß, worauf er sich vorbereiten muss.

Ich habe das MassTransit-Projekt diesbezüglich angepingt (sie durchlaufen die gleiche Änderung) und die Diskussion hier https://github.com/MassTransit/MassTransit/issues/314 geführt.

Es gibt einen separaten Test, um zu erkennen, dass es sich nicht um asynchrone Void-Methoden handelt (die die Killer sind).

@lahma Ich pinge nicht wirklich gerne, aber gibt es Pläne oder Schätzungen, wann (oder ob) dies getan und in die Wildnis entlassen wird?

einverstanden - ich würde wirklich gerne damit anfangen

:+1: für ein paar Infos :)

Sie können den Fortschritt des Zweigs quartznet-3 https://github.com/quartznet/quartznet/tree/quartznet-3 verfolgen, es gibt jetzt noch nächtliche Builds, aber die API hat bereits viele Änderungen, um das Modell zu unterstützen.

Tut mir leid, ein Schädling zu sein, aber ich frage mich nur, ob Sie einen Zeitplan haben, wann Quartz 3 fertig sein wird. Mein Projekt hängt im Moment von der Fertigstellung dieses Features ab. Es scheint, dass Sie derzeit keine HttpClient-Anforderung in einem Quartz-Job ausführen können, wenn Sie synchrones Verhalten benötigen und ein Intervall haben, das kleiner ist als die Umlaufzeit der Http-Anforderung. Quartz "wartet" nicht auf den Rückgabewert und Ihre Anforderungen beginnen sich zu überschneiden. Ich nehme an, dieser neue asynchrone Jobtyp würde dieses Problem lösen? Ich verwende Mono und ich denke, HttpClient ist die einzige Option und es gibt nur asynchrone Anfragen.

Asyn-Jobs selbst sind vielleicht nicht die Wunderwaffe. Sie können das synchrone Ergebnis für HttpClient erzwingen, indem Sie client.GetAsync(_address).Result verwenden, das die Anforderung auswertet. Funktioniert das für dich? Sie haben auch die Überlappung von Jobs erwähnt, die mit [DisallowConcurrentExecution] behandelt werden kann, wenn das hilft.

Und ja, ich bin der ursprünglichen Frage ausgewichen, da ich keine Versprechungen machen möchte, die schwer zu halten wären.

Ich schätze Ihre schnelle (und ehrliche) Antwort. Kannst du mir ein Viertel nennen, bis zu dem du derzeit hoffst, es zu veröffentlichen? Wie Q3 2016, Q4 2016, Q1 2017?

Ich habe innerhalb von 2 Minuten nach dem Posten eine ähnliche Option gefunden wie die, die Sie bereitgestellt haben (passiert immer?). Ich habe es bereits funktioniert, also keine Sorge.

Ich sollte die Gelegenheit nutzen, um meiner Liebe zu Quartz Ausdruck zu verleihen. Ich habe es verwendet, um ein Microframework zu erstellen, um das Konfigurieren, Schreiben und Bereitstellen von Microservices zu erleichtern, und Quartz ist das Herzstück davon. Ich hatte das Glück, darauf zu stoßen, bevor ich etwas umschrieb, das halb so gut war. Vielen Dank an alle Mitwirkenden für die Bereitstellung.

Ich denke, wir können in den nächsten Wochen mit der Ausgabe von Alpha-Qualität beginnen. Die Grundlagen sind solide (Trigger-Berechnungen, Datenverarbeitung im Jobspeicher - sie wurden nicht geändert), aber wenn sich die Arbeit um async/await und Thread-Pools und API weiterentwickelt, wird ihre Stabilität verbessert kann nicht garantiert werden. Remoting wird ebenfalls fallen gelassen und muss zusätzlich zur HTTP/Web-API neu geschrieben werden, sodass die Fernsteuerung des Schedulers nicht Teil der ersten Drops sein wird.

Gibt es hierzu Neuigkeiten ?

Alpha-Build ohne Remoteverwaltung sollte ziemlich bald für nuget.org-Feeds machbar sein.

Alpha 1 ist bei NuGet gelandet, bitte sehen Sie sich die Ankündigung an . Es gibt noch viel zu tun, aber jetzt sollte es einfacher sein, in die Reifen zu treten.

Ich schließe dieses Thema vorerst, vielen Dank für Ihre Geduld!

Erstaunliche Szenen! Danke - ich kann es kaum erwarten, dies zu versuchen

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen