Geringe Kopplung bei der Verarbeitung großer Datenmengen

8

Normalerweise erreiche ich eine niedrige Kopplung, indem ich Klassen erstelle, die Listen, Mengen und Karten zwischen ihnen austauschen. Jetzt entwickle ich eine Java-Batch-Anwendung und kann nicht alle Daten in eine Datenstruktur einfügen, da nicht genügend Speicher vorhanden ist. Ich muss einen Datenblock lesen und verarbeiten und dann zum nächsten übergehen. Eine niedrige Kopplung ist also viel schwieriger, da ich irgendwo prüfen muss, ob noch Daten zu lesen sind usw.

Was ich jetzt benutze ist:

Quelle -> Prozess -> Bestehen

Die Klassen, die verarbeitet werden, müssen die Quellklassen fragen, ob mehr Zeilen zu lesen sind.

Was sind die besten Praktiken und / oder nützlichen Muster in solchen Situationen?

Ich hoffe ich erkläre mich, wenn nicht sag es mir.

Виталий Олегович
quelle
3
Eine der Möglichkeiten, eine niedrige Kopplung zu erreichen, besteht darin, ein gutes Kommunikationsprotokoll zwischen
Quellklassen
3
Ich denke, Sie möchten vielleicht eine Nachrichtenwarteschlange verwenden - eine Art Datenbus -, damit Ihre Klassen Dinge in Warteschlangen stellen und aus Warteschlangen ziehen, anstatt direkt zu interagieren.
Murph
@Murph gibt es eine einfache Möglichkeit oder eine gute Java-Bibliothek, eine Nachrichtenwarteschlange zu verwenden?
Виталий Олегович
@vitalik - Ich bin ein .NET-Entwickler und fühle mich immer noch mit Nachrichtenwarteschlangen im Allgemeinen so, dass ich nicht wirklich in der Lage bin, eine sichere Antwort zu geben (daher meine Antwort auf einen Kommentar)
Murph
1
@ Murphy ok, trotzdem danke! Ich denke, ich werde auch anfangen, Warteschlangen zu studieren!
Виталий Олегович

Antworten:

7

Aus den Kommentaren geht hervor, dass Sie Java verwenden. Schauen Sie sich verschiedene Queue- Implementierungen an. Insbesondere Blocking ist für nützliche Producer-Consumer - Szenarien. Sie können zwei Warteschlangen haben: eine zwischen Source (Datenproduzent) und Process (Datenkonsument) und eine zwischen Process (Ergebnisproduzent) und Persist (Ergebniskonsument).

Mit Blockierungswarteschlangen mit begrenzter Kapazität ist es ziemlich einfach, effiziente Systeme zu implementieren (der Engpass wird zu 100% mit Daten versorgt), wobei immer noch nur eine begrenzte Menge an Speicher verwendet wird, unabhängig davon, wie viele Daten vorhanden sind.

Joonas Pulakka
quelle
Ihre Lösung ist sehr gut. Aber was passiert, wenn ich eine Warteschlange mit begrenzter Kapazität verwende und die Warteschlange voll ist und ich versuche, etwas hinzuzufügen?
Виталий Олегович
@vitalik Dann müssen Sie eine Strategie festlegen, z. B. das vorübergehende Speichern der Daten in einer In-Memory-Datenbank oder das Festlegen einer anderen Lösung dieser Art.
Martijn Verburg
@MartijnVerburg ja, aber ich denke, es wäre einfacher, wenn es eine Möglichkeit gäbe, den Produzenten zum Schlafen zu bringen, bis mehr Platz in der Warteschlange verfügbar ist.
Виталий Олегович
1
@vitalik natürlich gibt es die möglichkeit (einen produzenten zu schlafen) man muss es einfach machen. Einige Warteschlangen können so konfiguriert werden, dass sie blockieren. Wenn ein Produzent versucht, sie in eine vollständige Warteschlange einzufügen, blockieren Sie sie einfach und schlafen / drehen (achten Sie auf welche) in der Warteschlange, um Speicherplatz zu haben.
SDG
1
@vitalik: Siehe z. B. BlockingQueue.put docs: Fügt das angegebene Element in diese Warteschlange ein und wartet, falls erforderlich, bis Speicherplatz verfügbar ist. Einfach und bequem! :)
Joonas Pulakka
2

Eine blockierende Warteschlange (von Joonas Pulakka) ist die schwere Antwort. Eine einfachere Antwort könnte funktionieren. Wenn Sie alle Daten in der Quelle gespeichert haben, können Sie einfach einen Verweis an den Prozessor übergeben und die Daten einfach aus der Quelle abrufen. Natürlich ist dies wahrscheinlich das, was Sie in der Vergangenheit getan haben. Möglicherweise befinden sich nicht alle Daten im Speicher der Quelle, und Sie erhalten möglicherweise nicht die gewünschte niedrige Kopplung.

Der nächste Schritt wäre die Verwendung einer Enumerator- oder Iterator-Schnittstelle. (Iteratoren sind in Java häufiger anzutreffen, obwohl diese removeMethode meistens nur eine Nusance ist.) Der Prozessor bezieht den Iterator von der Quelle und ruft die Methoden dann auf, bis er fertig ist. Wenn die Quelle Terrabyte an Daten von irgendwoher bezieht, kann jeder Anruf eine Weile dauern. Wenn Sie den Prozessor jedoch in den Ruhezustand versetzen, bis sich ohnehin etwas in der Warteschlange befindet, wird dies automatisch ausgeführt. Und wenn die Quelle dem Produzenten voraus ist, wartet die Quelle automatisch darauf, dass der Produzent anruft hasNextund next.

Wenn Sie andererseits möchten, dass die Quelle Daten so schnell wie möglich von ihrer Quelle abruft und sie bevorratet, bis der Prozessor aufholt und nicht darauf wartet, dass der Prozessor verarbeitet wird, dann die Warteschlange - und mehrere Threads - beginnen, wie eine gute, wenn auch kompliziertere Idee auszusehen. Jetzt kann die Quelle die Daten stapeln, wenn sie schneller ausgeführt werden können (ihre Grenze liegt vermutlich bei Festplatten-E / A), und der Prozessor kann die Größe der Stapel reduzieren, wenn sie schneller ausgeführt werden kann (ihre Grenze ist die Geschwindigkeit der Beständigkeit) Modul kann die Daten beibehalten).

RalphChapin
quelle