Scheduler für 100 Rechner

8. März 2005

Methode zur Ausführung von XML-Kommandos

XML-Kommandos können auch über eine spooler-Methode übergeben werden:


var xml_antwort = spooler.execute_xml( "<job name='xx'> <script...> </job>" );
var xml_antwort = spooler.execute_xml( "<show_state/"> );

Bei einem Fehler wird wie bei TCP keine Exception, sondern das Element <ERROR> geliefert. (ok?)

Jobs hinzufügen und entfernen

XML-Kommando  <job>

Arbeitet im Prinzip wie <add_job>, erlaubt es aber, ein echtes Fragment der Konfiguration zu übergeben. In der steht ja <job> und nicht <add_job>.

Wenn der Job bereits existiert, wird er überschrieben, als wäre er in einer <base>-Konfiguration festgelegt worden.

Methode spooler.job_exists( "jobname" )

Die Methode liefert true, genau dann wenn der Job bekannt ist.

Methode spooler.remove_job( "jobname" )

Die Methode entfernt einen Job.

Ändern oder Entfernen eines Jobs kann nur unter bestimmten Bedingungen erfolgen:

Aufträge entfernen

Methode job_chain.remove_order( order_id )

Entfernt den Auftrag. Wenn eine Task den Auftrag gerade verarbeitet, wird er nach der Verarbeitung gelöscht (unabhängig von seinem Zustand).

Methode spooler.start_process()

Damit kann ein Shell-Skript gestartet werden. Parameter wie bei <process>. Rückgabewert ist ein Objekt mit der Methode wait().


var process = spooler.start_process( "/xxx/shellscript", parameter, umgebungsvariablen );
process.timeout = 120;                      // Danach automatisches kill?
spooler_log.info( "pid=" + process.pid );

var is_terminated process.wait( 60.0 );    // 60s aufs Ende warten
spooler_log.info( process.exit_code );

add_pid() ist inklusive.

Shell-Skripte als Auftragsjobs zulassen

Auch ein Shell-Skript kann Auftragsgesteuert (order="yes") sein. Das Skript wird einfach für jeden Auftrag aufgerufen. Der Auftrag wird nicht übergeben.

(Später könnten wir Teile des Auftrags als Skript-Parameter oder mit Umgebungsvariablen übergeben.)

Auftragsjobs, die sich nach jedem Auftrag beenden

Wenn spooler_process() nicht implementiert ist, wird anscheinend false als Rückgabe angenommen. Der Auftrag landet dann im Fehlerzweig der Jobkette. Lösung: Fehlerzweig und Erfolgszeig gleich machen: job_chain.add_job( status, folgestatus, folgestatus, "einfacher job" ). Wenn spooler_process() fehlt, können Erfolg und Fehler ohnehin nicht unterschieden werden.

<run_time> für Aufträge

Der <run_time>-Mechanismus wird auch für Aufträge gelten. Die Attribute begin=, end= und let_run= sind nicht erlaubt, statt dessen single_start=.

<period>, <date>, <weekdays>, <ultimos> und <holidays> sind erlaubt. (Der Name von <period> ist bei einem Auftrag nicht ganz richtig, es ist ja ein Zeitpunkt.)

Default ist <run_time once="yes"/>.

Wenn ein Auftrag einen Endzustand erreicht hat (add_end_state), dann prüfe ich, ob in <run_time> eine Wiederholung vorgesehen ist. Wenn ja, setze ich den Auftrag auf seinen Anfangszustand zurück. state_text lösche ich. Die Id bleibt dieselbe.

Wenn es keine Wiederholung gibt, lösche ich wie bisher den Auftrag. (Hier muss ich den Algorithmus erweitern, um dies sicher festzustellen, das war bei Jobs nicht nötig, da ich jede Mitternacht erneut prüfe.)

Run_time wird gesetzt mit: order.run_time_xml = "<run_time ...>".

Methoden für <run_time>: Wenn du willst, mache ich auch ein run_time-Objekt. Etwa so: order.run_time.add_start( "2005-04-01 12:00" ). Damit können die Startzeiten programmiert verändert werden. Das gilt dann auch für Jobs.

Verteilte Scheduler mit unabhängigen, lose gekoppelten Scheduler

Scheduler können sich an einen steuernden Scheduler anschließen. Wenn die Verbindung unterbrochen wird, versuchen die Scheduler sie wieder aufzubauen, laufen aber ungestört weiter. Jeder Scheduler hat seine eigene XML-Konfiguration, ist also unabhängig.

Der Begriff Nebenscheduler passt hier nicht gut, denn diese Scheduler können selbstständig laufen.

Wenn du willst, kann nach Unterbrechung der Verbindung der Scheduler eine neue Verbindung zu einem alternativen steuernden Scheduler aufbauen, der auf einem anderen Rechner läuft.

Was kann der steuernde Scheduler?

Verteilte Jobs

In seiner XML-Konfiguration können Jobs bekannt gegeben werden, die unter anderen Schedulern laufen. Die Jobskripte selbst werden im jeweiligen Scheduler definiert. (Erweiterung: Die Jobs werden vollständig vom steuernden Scheduler übertragen, was aber bei Java schwierig ist.)

Im steuernden Scheduler sieht das so aus:


    <job name="ein_job" order="yes" scheduler="host-a:4444"/>

Jobketten mit verteilten Jobs

Ein einem anderen Rechner zugewiesener Job kann in eine Jobkette aufgenommen werden. Der steuernde Scheduler übergibt Aufträge dann an den Nebenscheduler.

Dazu hält nur der steuernde Scheduler die Jobkette. Wenn ein Auftrag an einem externen Job übergeben werden soll, geschieht folgendes.

<run_time>?

Evtl. kann auch die <run_time> im steuernden Scheduler definiert sein (dann sollte sie nicht im Nebenscheduler definiert sein). Der steuernde Scheduler kann dann Tasks entsprechend seiner <run_time> starten, die dann unter dem Nebenscheduler laufen.

Zentrale Überwachung durch den steuernden Scheduler

Die HTML- oder PHP-Oberfläche kann durch eine TCP-Verbindung mit dem steuernden Scheduler Informationen über alle angeschlossenen Scheduler erhalten.

Eine neue Abfrage würde eine Liste der angeschlossenen Scheduler liefern (mit TCP-Adressen, so dass die HTML/PHP-Anwendung eigene Verbindungen zu den angeschlossenen Schedulern aufbauen kann).

Das könnte auf zwei verschiedene Weisen implementiert werden:

Verteilte Scheduler mit abhängigen, eng gekoppelten Nebenschedulern

Bei dieser zweiten Lösung wären die Nebenscheduler völlig vom steuernden Scheduler abhängig. Sie brauchen kaum eine XML-Konfiguration.

Sie dienen nur dazu, Jobs des steuernden Scheduler zu starten. Diese Jobs müssen in eigenen Prozessen laufen und arbeiten direkt mit dem steuernden Scheduler zusammen, nicht mit dem Nebenscheduler.

Das ist im Prinzip dasselbe wie bisher mit nur einem Scheduler, nur dass der Prozess, der den Job ausführt, auf einem anderen Rechner läuft. Alle Aufrufe (hin: spooler_process(), zurück: spooler_log.info()) werden über TCP, d.h. übers Netzwerk abgewickelt. Bei sehr vielen Aufrufen kann das eine Bremse sein.

Eng gekoppelte Scheduler sind einfacher zu realisieren als lose gekoppelte.

(Ein paar Dinge müssen noch geklärt werden. Zum Beispiel kann der steuernde Scheduler nicht die Datei für stderr vorgeben, wenn der Prozess auf einem anderen Rechner läuft; <kill_task> muss über den Nebenscheduler abgewickelt werden)

Anarchisches Modell

Es gibt keinen Häuptling.

Vorteile:

Nachteile:

Medizinmannmodell

Das ist die Kombination aus lose gekoppelten und anarchischem Modell.

Natürlich gibt es fast nur Vorteile:

Und wenn der Häuptling nicht läuft:

Wenn der Häuptling wieder läuft:

Protokolldateien über NFS oder SMB

Möglicherweise können wir einen zentralen Fileserver voraussetzen, auf dem per NFS die Protokolle geschrieben werden. Dann könnte der Indianer, der den Auftrag beendet, selbst das Auftragsprotokoll in die Datenbank schreiben.

Schwieriger ist es mit dem zentralen Hauptprotokoll: Es ist nicht klar, ob NFS zuverlässig von mehreren Rechnern aus eine Datei gleichzeitig fortschreiben kann.

Falls einmal Scheduler unter Windows hinzukommen sollten, würde das nicht funktionieren. Man müsste dann auf SMB umsteigen.

Vergleich der Methoden für verteilte Jobs

Jeder Scheduler kann selbständig arbeiten mit lokalen Jobs und lokalen Jobketten. Hier werden nur Jobketten mit verteilten Jobs betrachtet.

Für alle Methoden gilt: Wenn ein Scheduler abbricht, egal ob Häuptling oder Indianer, dann sterben die von ihm gesteuerten Jobs. Wenn der Scheduler neu startet, liest er den Zustand aus der Datenbank:

 

 

Monarchisch
mit Häuptling, eng gekoppelt
Fürstentümer
mit Häuptling, lose gekoppelt
Anarchisch
ohne Häuptling
Konstitutionelle Monarchie
Der Häuptling steuert alles unmittelbar. Er hat volle Kontrolle. Er führt die Protokolle und hält die Objekte (Task, Order). Der Indianer steuert den Job unmittelbar. Er führt die Protokolle und hält die Objekte (Task, Order). Jeder Scheduler steuert seine Jobs und kann Jobs unter anderen Schedulern starten (ohne Rückmeldung) und kann Aufträge übergeben (ebenfalls ohne Rückmeldung). Anarchisch mit optionalen Häuptling (eher einem Medizinmann). Alles läuft auch ohne Häuptling, nur gibt es dann keine zentrale Auskunft.

Was passiert beim Abbruch eines Indianers? Ein gestarteter Job läuft zuende, denn er kommuniziert nur mit dem Häuptling. Dieser Job kann auch Aufträge verarbeiten. Dazu wird der Indianer nicht gebraucht.

Der Häuptling kann keine Jobs starten oder killen. Nach dem Neustart des Indianers ist das wieder möglich (der Indianer muss dann einen kill auf einen ihm selbst unbekannten Prozess ausführen dürfen).

Der Job wird abbrechen, weil die Verbindung zu seinem Scheduler unterbrochen ist.

Der Indianer verhält sich wie ein einfacher Scheduler: Beim Neustart liest der Scheduler den Zustand aus der Datenbank und setzt fort.

Oder: Beim Neustart holt sich der Indianer den Zustand vom Häuptling.

Wie links (ohne die Häuptlingsoption) Wie links
 
→ Harmlos nach Neustart → Wie Abbruch des bisherigen Schedulers → Wie Abbruch des bisherigen Schedulers → Wie Abbruch des bisherigen Schedulers

Was passiert beim Abbruch des Häuptlings? Der Häuptling ist wesentlich, ohne ihn läuft nichts (bis auf die selbständigen Aktivitäten der Indianer). Das ist wie bisher: ein Scheduler steuert alle Jobs, nur eben über mehrere Rechner hinweg. Die Indianer führen ganze Jobs oder Auftragsschritte selbständig durch.

Der Häuptling wartet nach einem Neustart auf die Anrufe der Indianer (oder er ruft die Indianer an, wenn er ein Melderegister hat). Der Häuptling erhält dann von den Indianern den Zustand der ausgeliehenen Aufträge.

Nichts passiert. Die Indianer teilen dem Häuptling nichts mehr mit, laufen aber weiter.

Der Häuptling fordert nach seinem Neustart von den Indianern aktuelle Informationen an. Die Protokolle werden (soweit noch geöffnet) erneut übertragen.

 
→ Wie Abbruch des bisherigen Schedulers
(betrifft nur die verteilten Jobs)
→ Harmlos nach Neustart → Harmlos. Während des Abbruchs gibt es keine zentrale Information.

Jobstart Entfernte Prozesse werden wie lokale Prozesse behandelt.
  • Prozessstart über Indianer
  • Prozess verbindet sich direkt mit dem Häuptling
  • Indianer führt kill_process durch
Entfernte Prozesse werden vom jeweiligen Indianer gesteuert. Der Indianer teilt dem Häuptling das Ende der Task mit, vielleicht auch den Zustand waiting_for_order. Jeder Scheduler kann einen Job unter einem anderen Scheduler starten. Der andere Scheduler teilt dem ersten nichts mit. Jeder Indianer und der Häuptling können Jobs unter anderen Schedulern starten.

Jobkette mit verteilten Jobs Wie bisheriger Scheduler: der entfernte Prozess ist wie ein lokaler direkt angebunden. Der Häuptling ist genauso über den Job informiert als wäre es ein lokaler Prozess. Nur der Häuptling kennt die Jobkette. Der Häuptling leiht den Auftrag an den Indianer aus (über TCP oder Datenbank). Wenn der Auftrag verarbeitet ist (spooler_process() endet), gibt der Indianer den Auftrag an den Häuptling zurück (inkl. payload, state usw.) Jeder beteiligte Scheduler kennt die Jobkette. Ein Scheduler kann den Auftrag an den nächsten der Jobkette übertragen (über TCP oder Datenbank). Anschließend kümmert sich dieser Scheduler um den Auftrag. Der erste Scheduler vergisst den Auftrag. Wie links

Ändern der Jobkette Nur der Häuptling muss neu gestartet werden. Nur der Häuptling muss neu gestartet werden. Alle betroffenen Scheduler müssen neu gestartet werden. Wie links

Auftragsprotokoll Der Häuptling erstellt unmittelbar das Protokoll. Es ist stets aktuell. Der Häuptling bekommt das Protokoll des Jobschritts des Indianers am Ende des Jobsschritts via TCP.

Es ist auch einstellbar, dass der Häuptling jede Protokollzeile sofort bekommt. Damit ist das Auftragsprotokoll stets aktuell. (Der TCP-Verkehr kann optimiert werden, wenn die Protokollzeilen kontinuierlich über eine eigene Verbindung übertragen werden.)

Es gibt keine zentrale Stelle für das Auftragsprotokoll (evtl. gemeinsamer Dateizugriff über NFS/SMB möglich?). Für jeden Job, den der Auftrag durchläuft, gibt es ein Prokokoll. Man könnte nach jedem Jobschritt das bisherige Protokoll aus dem BLOB lesen und um den neuen Jobschritt erweitert zurückschreiben. Oder die Fragmente separat speichern (1:n) und von der Oberfläche zusammensetzen lassen. Solange der Häuptling läuft, kann er das Auftragsprotokoll führen. Wenn er nicht läuft, haben wir Fragmente.

Hauptprotokoll Es gibt nur ein Hauptprotokoll. (bis auf die selbstständigen, vom Häuptling unabhängigen Aktivitäten der Indianer). Jeder Indianer hat sein eigenes Hauptprotokoll. Bestimmte Operationen (oder alle) kann ein Indianer auch dem Häuptling melden, der sie in sein Hauptprotokoll übernimmt. Kein zentrales Hauptprotokoll. Solange der Häuptling läuft, haben wir ein zentrales Hauptprotokoll. Sonst einzelne Hauptprotokolle.

Erste Aufwandsschätzung

execute_xml() 1 Tag
Job löschen oder ändern 3 Tage
Shell-Skripte als Auftragsjobs und Auftragsjobs, die sich nach jedem Auftrag beenden 1 Tag
spooler.start_process() wie <process> 5 Tage
<run_time> für Aufträge (ohne Methoden zum Ändern) 4 Tage
API für <run_time> 5 Tage
Scheduler-Monarchie 12 Tage
Scheduler-Anarchie (ohne Häuptling), mit zusätzlichen Statusanzeigen in der Datenbank 12 Tage
Scheduler-Anarchie mit Medizinmann, je nach Umfang 40-50 Tage

 


Dipl.-Inform. Joacim Zschimmer          j@zsch.de         (030) 3470 2520         Zschimmer GmbH