Scheduler für 100 Rechner
8. März 2005
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?)
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.
Die Methode liefert true, genau dann wenn der Job bekannt ist.
Die Methode entfernt einen Job.
Ändern oder Entfernen eines Jobs kann nur unter bestimmten Bedingungen erfolgen:
Entfernt den Auftrag. Wenn eine Task den Auftrag gerade verarbeitet, wird er nach der Verarbeitung gelöscht (unabhängig von seinem Zustand).
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.
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.)
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.
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.
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.
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"/>
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.
payload
) an den Nebenscheduler aus.
Er tut dies nur, wenn der Nebenscheduler den Auftrag sofort verarbeiten kann.
spooler_process()
aus).
payload
)
an den steuernden Scheduler zurück.
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.
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:
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)
Es gibt keinen Häuptling.
Vorteile:
Nachteile:
Das ist die Kombination aus lose gekoppelten und anarchischem Modell.
Natürlich gibt es fast nur Vorteile:
<show_state>
usw.).
Und wenn der Häuptling nicht läuft:
Wenn der Häuptling wieder läuft:
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.
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.
|
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. |
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