Frage

Thema:

Ich versuche, eine grundlegende Jobplanung in Java zu implementieren, um wiederkehrende, dauerhaft geplante Aufgaben zu verarbeiten (für ein persönliches Lernprojekt).Ich möchte keine (gebrauchsfertigen) Bibliotheken wie Quartz/Obsidian/Cron4J/etc. verwenden.

Zielsetzung:

  • Der Job muss persistent sein (um das Herunterfahren des Servers zu bewältigen)
  • Die Auftragsausführungszeit kann bis zu ca. 2–5 Minuten dauern.
  • Verwalten Sie eine große Menge an Aufträgen
  • Multi Thread
  • Leicht und schnell ;)

Alle meine Jobs befinden sich in einer MySQL-Datenbank.

JOB_TABLE (id, name, nextExecution,lastExecution, status(IDLE,PENDING,RUNNING))

Schritt für Schritt:

  1. Rufen Sie jeden Job ab von „JOB_TABLE" Wo “nextExecution > now” AND “status = IDLE“.Dieser Schritt wird alle 10 Minuten von einem einzelnen Thread ausgeführt.

  2. Für jeden abgerufenen Job habe ich einen neuen Thread in a eingefügt ThreadPoolExecutor dann aktualisiere ich den Jobstatus auf „PENDING" in meinem "JOB_TABLE”.

  3. Wenn der Job-Thread ausgeführt wird, aktualisiere ich den Jobstatus auf „RUNNING”.

  4. Wenn der Job abgeschlossen ist, aktualisiere ich die lastExecution Mit der aktuellen Uhrzeit habe ich eine neue eingestellt nextExecution Zeit und ich ändere den Jobstatus in „IDLE”.

Wenn der Server startet, füge ich jeden ausstehenden/ausgeführten Job in die Datei ein ThreadPoolExecutor.

Frage/Beobachtung:

  • Schritt 2 :Wird der ThreadPoolExecutor eine große Menge an Threads verarbeiten (~20000)?
  • Sollte ich eine NoSQL-Lösung anstelle von MySQL verwenden?
  • Ist es die beste Lösung für einen solchen Anwendungsfall?

Dies ist ein Entwurf, es gibt keinen Code dahinter.Ich bin offen für Anregungen, Kommentare und Kritik!

War es hilfreich?

Lösung

Ich habe eine ähnliche Aufgabe wie Ihre Aufgabe bei einem echten Projekt durchgeführt, jedoch in .NET.Folgendes fällt mir in Bezug auf Ihre Fragen ein:

Schritt 2 :Kann der ThreadPoolExecutor eine große Menge an Threads verarbeiten (~20.000)?

Wir haben festgestellt, dass der integrierte Thread-Pool von .NET der schlechteste Ansatz war, da es sich bei dem Projekt um eine Webanwendung handelte.Grund:Die Webanwendung verlässt sich auf den integrierten Thread-Pool (der statisch ist und daher für alle Zwecke innerhalb des laufenden Prozesses gemeinsam genutzt wird), um jede Anforderung in einem separaten Thread auszuführen und gleichzeitig ein effektives Thread-Recycling sicherzustellen.Die Verwendung desselben Thread-Pools für unsere interne Verarbeitung würde diesen erschöpfen und keine freien Threads für Benutzeranfragen hinterlassen oder deren Leistung beeinträchtigen, was inakzeptabel wäre.

Da Sie anscheinend ziemlich viele Jobs ausführen (20.000 sind viel für eine einzelne Maschine), sollten Sie auf jeden Fall nach einem benutzerdefinierten Thread-Pool suchen.Sie müssen jedoch keine eigenen Lösungen schreiben, ich wette, es gibt fertige Lösungen, und das Schreiben einer solchen geht weit über das hinaus, was Ihr Studienprojekt erfordern würde* siehe die Kommentare (Wenn ich das richtig verstehe, machen Sie ein Schul- oder Universitätsprojekt).

Sollte ich eine NoSQL-Lösung anstelle von MySQL verwenden?

Kommt darauf an.Sie müssen den Auftragsstatus natürlich gleichzeitig aktualisieren, sodass Sie von mehreren Threads aus gleichzeitig auf eine einzelne Tabelle zugreifen können.Datenbanken lassen sich ziemlich gut darauf skalieren, vorausgesetzt, Sie haben es richtig gemacht.Folgendes beziehe ich mich darauf, dies richtig zu machen:

  • Entwerfen Sie Ihren Code und zwar so, dass sich jeder Job nur auf seine eigene Teilmenge von Zeilen in der Datenbank auswirkt (dazu gehören auch andere Tabellen).Wenn Sie dazu in der Lage sind, benötigen Sie keine expliziten Sperren auf Datenbankebene (in Form von Transaktionsserialisierungsebenen).Sie können sogar eine liberale Serialisierungsstufe erzwingen, die möglicherweise schmutzige oder Phantom-Lesevorgänge zulässt – was zu einer schnelleren Leistung führt.Aber in acht nehmen, müssen Sie sorgfältig sicherstellen, dass keine Jobs in denselben Zeilen gleichzeitig ausgeführt werden.Dies ist in realen Projekten schwer zu erreichen, daher sollten Sie wahrscheinlich nach alternativen Ansätzen für die Datenbanksperre suchen.

  • Verwenden Sie den geeigneten Transaktionsserialisierungsmodus. Der Transaktionsserialisierungsmodus definiert das Sperrverhalten auf Datenbankebene.Sie können festlegen, dass die gesamte Tabelle, nur die betroffenen Zeilen oder gar nichts gesperrt werden.Setzen Sie es mit Bedacht ein, denn jeder Missbrauch könnte die Datenkonsistenz, -integrität und die Stabilität der gesamten Anwendung oder des Datenbankservers beeinträchtigen.

  • Da ich mit NoSQL-Datenbanken nicht vertraut bin, kann ich Ihnen nur raten, sich über die Parallelitätsfunktionen zu informieren und sie Ihrem Szenario zuzuordnen.Möglicherweise finden Sie am Ende eine wirklich geeignete Lösung, müssen dies jedoch entsprechend Ihren Anforderungen prüfen.Ihrer Beschreibung zufolge müssen Sie gleichzeitige Datenoperationen für denselben Objekttyp unterstützen (was das Analogon für eine Tabelle ist).

Ist es die beste Lösung für einen solchen Anwendungsfall?

Ja und nein.

  • Ja, denn Sie werden auf eine der schwierigen Aufgaben stoßen, mit denen Entwickler in der realen Welt konfrontiert sind.Ich habe mit Kollegen zusammengearbeitet, die mehr als dreimal so viel Erfahrung hatten wie ich, und sie waren bei Multithreading-Aufgaben zurückhaltender als ich, sie hassten das wirklich.Wenn Sie das Gefühl haben, dass dieser Bereich für Sie interessant ist, spielen Sie damit, lernen Sie und verbessern Sie sich so viel wie nötig.

  • NEIN, denn wenn Sie an einem realen Projekt arbeiten, brauchen Sie etwas Zuverlässiges.Wenn Sie so viele Fragen haben, brauchen Sie natürlich Zeit, um zu reifen und eine stabile Lösung für eine solche Aufgabe zu finden.Multithreading ist aus vielen Gründen ein schwieriges Thema:

    • Es ist schwer zu debuggen
    • Es führt zu vielen Fehlerquellen, die Sie alle kennen müssen
    • Es könnte für andere Entwickler schwierig sein, Ihren Code zu unterstützen oder mit ihm zu arbeiten, es sei denn, Sie halten sich an allgemein anerkannte Regeln.
    • Die Fehlerbehandlung kann schwierig sein
    • Das Verhalten ist unvorhersehbar/undeterministisch.

    Es gibt bestehende Lösungen mit hohem Reifegrad und Zuverlässigkeit, die für reale Projekte den bevorzugten Ansatz darstellen.Der Nachteil besteht darin, dass Sie sie erlernen und prüfen müssen, wie anpassbar sie an Ihre Bedürfnisse sind.

Wie auch immer, wenn Sie es auf Ihre Art und Weise tun und Ihre Leistung dann auf ein echtes Projekt oder ein eigenes Projekt übertragen müssen, kann ich Ihnen raten, dies auf eine steckbare Art und Weise zu tun.Abstraktion nutzen, Programmierung an Schnittstellen und andere Praktiken, um Ihre eigene spezifische Implementierung von der Logik zu entkoppeln, die die geplanten Jobs festlegt.Auf diese Weise können Sie Ihre API an eine bestehende Lösung anpassen, falls dies zu einem Problem wird.


Und zuletzt, aber nicht zuletzt, ich habe auf Ihrer Seite keine Vorhersagen zur Fehlerbehandlung gesehen.Überlegen und recherchieren Sie, was zu tun ist, wenn ein Job scheitert.Fügen Sie in einem solchen Fall zumindest den Status „FEHLGESCHLAGEN“ oder etwas hinzu, das bestehen bleibt.Die Fehlerbehandlung ist bei Threads schwierig. Seien Sie daher bei Ihrer Recherche und Vorgehensweise gründlich.

Viel Glück

Andere Tipps

Sie können die maximale Poolgröße mit ThreadPoolExecutor#setMaximumPoolSize(int) deklarieren.Als Integer.MAX ist größer als 20000, dann ist es technisch gesehen ja möglich.

Die andere Frage ist, ob Ihr Computer so viele auszuführende Threads unterstützen würde.Sie müssen genügend RAM bereitstellen, damit jeder Schritt auf dem Stapel zugewiesen werden kann.

Dir sollte kein Problem sein Adressieren Sie ca. 20.000 Threads Auf modernen Desktops oder Laptops könnte es jedoch ein Problem sein, auf mobilen Geräten.

Aus Dokument:

Kern- und maximale Poolgrößen

Ein ThreadPoolexecutor passt automatisch die Poolgröße an (siehe GetPoolSize ()) gemäß den von CorePoolSize festgelegten Grenzen (siehe GetCorepoolsize ()) und MaximumpoolSize (siehe GetMaximumpoolsize ()).Wenn eine neue Aufgabe in der Methode Execute (java.lang.runnable) eingereicht wird und weniger als Corepoolsize -Threads ausgeführt werden, wird ein neuer Thread erstellt, um die Anforderung zu verarbeiten, auch wenn andere Arbeiter -Threads im Leerlauf sind.Wenn es mehr als Corepoolsize gibt, aber weniger als Maximumpoolsize -Threads ausgeführt werden, wird nur ein neuer Thread erstellt, wenn die Warteschlange voll ist.Durch das Einstellen von CorepoolSize und Maximumphoolsize erstellen Sie einen Threadpool mit fester Größe.Durch Einstellen von Maximumpoolsize auf einen im Wesentlichen unbegrenzten Wert wie Integer.max_Value lassen Sie den Pool eine willkürliche Anzahl von gleichzeitigen Aufgaben aufnehmen.In der Regel werden die Kern- und maximalen Poolgrößen nur bei der Konstruktion festgelegt, können jedoch auch dynamisch unter Verwendung von SetCorepoolSize (int) und setMaximumpoolsize (int) geändert werden.

Mehr

Über die DB.Erstellen Sie eine Lösung, die nicht von der DB-Struktur abhängt.Dann können Sie zwei Umgebungen einrichten und messen.Beginnen Sie mit der Technologie, die Sie kennen.Aber bleiben Sie offen für andere Lösungen.Zu Beginn sollte die Relations-DB mit der Performance Schritt halten.Und wenn Sie es richtig verwalten, sollte es später kein Problem mehr sein.NoSQL wird verwendet, um mit wirklich großen Datenmengen zu arbeiten.Am besten erstellen Sie jedoch beides und führen einige Leistungstests durch.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top