1 Themenübersicht
Ein Dienst stellt im Allgemeinen zentrale Funktionen bereit, die von Anwendungen auf beliebigen Application-Servern genutzt werden können.
Beispiel:
Der Lagerlogistikserver ist ein Dienst.
Der Planungsserver ist ein Dienst.
Die Semiramis-System-Engine stellt die Infrastruktur für Dienste bereit.
2 Zielgruppe
- Entwickler
- Technische Berater
3 Beschreibung
Die Semiramis-System-Engine stellt eine Infrastruktur für Dienste bereit. Diese Infrastruktur hat die folgenden Eigenschaften:
- Zentrale Verwaltung der laufenden Dienste
- Aufruf eines Dienstes ohne Angabe eines Application-Servers
- Fehlerbehandlung bei Aufrufen
- Detaillierte Fehlermeldungen
- Fehler werden sofort zurückgemeldet
- Anfragen können asynchron ausgeführt werden
- Mehr Parallelität
- Bessere Auslastung der Ressourcen
Die folgenden Abschnitte beschreiben die Dienstinfrastruktur.
3.1 Dienste und Dienstinstanzen
Dienste stellen innerhalb eines Semiramis-Systems für Funktionen bereit, die von beliebigen Anwendungen genutzt werden können. Im Unterschied zu Logik-Klassen wird, die aufgerufene Funktion nicht in der Session (Thread) der aufrufenden Anwendung ausgeführt sondern in der Session des Dienstes.
Beispiel:
Der Lagerlogistik-Server für die Datenbank XYZ44000 ist ein Dienst.
Eine Dienstinstanz ist ein auf einem Application-Server gestarteter Dienst. Der gleiche Dienst kann auf einem oder mehreren Application-Servern laufen. Es kann also zu einem Dienst mehrere Dienstinstanzen geben. Eine Dienstinstanz besteht aus der Schnittstelle, die der Dienst für Anfragen bereitstellt und aus einen oder mehreren Verarbeitungsaufträgen. Anfragen an die Dienstinstanz verteilt die Schnittstelle an die Verarbeitungsaufträge, die die Anfragen verarbeiten.
Anwendungen kommunizieren ausschließlich über die vom Dienst bereitgestellte Schnittstelle. Eine Dienstinstanz gibt es für jeden Dienst maximal einmal pro Application-Server. Anwendungen können Funktionen von Dienstinstanzen aufrufen, auch wenn die Anwendungen auf einem anderen Application-Server laufen.
Beispiel für Dienste in einem System
In dem obigen Beispiel läuft der Dienst X auf dem Application-Server 1 und 2. Im Application Server 1 verarbeiten zwei Verarbeitungsaufträge die Anfragen des Dienstes X. Im Application-Server 2 verarbeitet nur ein Verarbeitungsauftrag die Anfragen des Dienstes X. Der Dienst Y läuft mit einem Verarbeitungsauftrag nur auf Application-Server 1 und nicht auf Application-Server 2.
Beispiel:
Der auf dem Application-Server ABC gestartete Lagerlogistik-Server für die Datenbank XYZ ist eine Dienstinstanz. Eine Anwendung läuft auf dem Application-Server DEF und kann den Lagerlogistik-Server auf ABC für die Datenbank XYZ aufrufen.
Die Informationen über die laufenden Dienstinstanzen werden zentral verwaltet. Funktionen eines Dienstes können damit aufgerufen werden, ohne dass die aufrufende Anwendung wissen muss, auf welchem Application-Server die Dienstinstanz läuft.
Beispiel:
Es ist muss nicht im Customizing gespeichert werden auf welchem Rechner der Lagerlogistikserver für die Datenbank XYZ44000 läuft. Funktionen des Lagerlogistikservers für die Datenbank XYZ44000 können ohne Angabe eines Application-Servers aufgerufen werden.
Wenn für einen Dienst mehr als eine Dienstinstanz existiert, werden die Aufrufe an die Dienstinstanzen möglichst gleichmäßig verteilt. Durch die gleichmäßige Verteilung der Aufrufe skaliert ein Dienst linear mit der Anzahl der verfügbaren Dienstinstanzen.
3.2 Schnittstelle eines Dienstes
Anwendungen können die von einem Dienst bereitgestellten Funktionen aufrufen. Einer Funktion eines Dienstes wird mit den folgenden Parametern aufgerufen:
Parameter | Beschreibung |
int
action |
Eindeutige Identifikation der vom Dienst aufgerufenen Funktion. Welche Funktionen einer ein Dienst hat, ist abhängig von der Implementierung des Dienstes. |
CisParameterList
parameters |
Parameterliste mit Key-Value-Pairs. Welche Parameter einer Funktion eines Dienstes übergeben werden müssen, ist abhängig von der Implementierung des Dienstes. |
Die Dienstinstanz berechnet aus der Action und den Parametern das Ergebnis des Funktionsaufrufs. Das Ergebnis wird als CisParameterList zurückgeben.
Ein Dienst ist identifiziert durch die Klasse, die den Dienst implementiert, und einer Id, die eine beliebige Zeichenkette ist.
Für den Aufruf der Funktion eines Dienstes werden somit die folgenden Parameter benötigt:
- Application-Server (Optional)
- Klasse des Dienstes
- Id
- Action
- Parameter
Wenn zusätzlich ein Application-Server angegeben wird, dann wird eine Dienstinstanz auf genau diesem Application-Server aufgerufen. Wenn kein Application-Server abgeben wird, wird eine beliebige Dienstinstanz des Dienstes ausgewählt.
Im Unterschied zum Aufruf einer Logikfunktion können beim Aufruf einer Funktion eines Dienstes beispielsweise die folgenden Fehler auftreten:
- Es wurde keine Dienstinstanz des Dienstes gestartet.
- Der Dienst verarbeitet keine Anfragen.
- Der Application-Server mit der Dienstinstanz wurde während der Anfragebearbeitung beendet.
- …
Alle diese Fehlermöglichkeiten müssen beim Aufruf einer Funktion berücksichtigt werden. Insbesondere wenn die Funktion persistente Daten verändert, ist es für die aufrufende Anwendung im Fehlerfall wichtig zu wissen, ob die Funktion ausgeführt worden ist oder nicht.
Weitere Informationen zu den Fehlerzuständen beim Aufruf einer Funktion finden Sie im Abschnitt „Aufruf von Funktionen“.
3.3 Programmierschnittstelle
Die Programmierschnittstelle für Dienste besteht aus der Schnittstelle, die Anwendungen benutzen, um Funktionen eines Dienstes aufzurufen und in die Schnittstelle zum Entwickeln von Diensten.
3.3.1 Statusübergänge bei Funktionsaufrufen
Eine Funktion eines Dienstes von jedem beliebigen Application-Server aufgerufen werden. Der Aufruf einer Funktion eines Dienstes erzeugt ein Request, das den aktuellen Zustand des Aufrufs enthält. Beim Aufruf einer Funktion gibt es den folgenden Ablauf:
Ablauf beim Aufruf einer Funktion eines Dienstes
Nach dem Absenden des Funktionsaufrufs an den Dienst, wartet die aufrufende Anwendung, ob die Dienstinstanz das Request innerhalb des Anfragetimeouts bearbeitet hat. Wenn die Anfrage bearbeitet wurde, so wird der Status des Requests von der Dienstinstanz auf Erledigt (DONE) gesetzt. Der Anfragetimeout sollte so gewählt sein, dass die Dienstinstanz im Normalfall alle Requests innerhalb des Anfragetimeouts bearbeiten kann.
Wenn die Dienstinstanz durch außergewöhnliche Umstände ein Request nicht innerhalb des Anfragetimeouts bearbeiten kann, so fordert die aufrufende Anwendung normalerweise den Abbruch der Bearbeitung des Requests an. Wenn die Dienstinstanz diese Abbruchanforderung verarbeitet, dann wird der Status des Requests auf Abgebrochen (ABORTED) gesetzt. Wenn die Dienstinstanz die bereits angefangene Anfrageberarbeitung innerhalb des Abbruchtimeouts erfolgreich abschließt, dann wird der Status des Requests auf Fertig (DONE) gesetzt. Wenn die Dienstinstanz auf die Abbruchanforderung innerhalb des Abbruchtimeouts nicht reagiert, so wechselt der Status des Requests auf Abbruchtimeout (ABORT_TIMEOUT). Der Abbruchtimeout ist im Allgemeinen deutlich länger als der Anfragetimeout. Der Abbruchtimeout sollte so lang gewählt werden, dass die Dienstinstanz genügend Zeit hat, den Request abzubrechen.
Beispiel:
Der Vorschlagswert für den Anfragetimeout ist 30s und für den Abbruchtimeout 5min.
Die Zustandsübergänge eines Requests sind in der folgenden Grafik dargestellt:
Zustandsübergänge beim Ausführen des Requests im Dienst
Die obigen Zustände werden im Folgenden beschrieben:
Konstantenname | Beschreibung |
SEND | Die Anfrage wurde an den Dienst gesendet, aber noch von keiner Dienstinstanz empfangen.
Mögliche Folgezustände sind: · RECEIVED · SVM_NOT_AVAILABLE · SERVICE_NOT_AVAILABLE |
RECEIVED | Die Anfrage wurde von der Dienstinstanz empfangen, aber noch keiner Bearbeitungswarteschlange zugeordnet.
Mögliche Folgezustände sind: · QUEUE_FULL · ASSIGNED · ABORTED |
ASSIGNED | Die Anfrage wurde einer Bearbeitungswarteschlange zugeordnet.
Mögliche Folgezustände sind: · IN_PROCESS · ABORTED |
IN_PROCESS | Die Anfrage aus der Bearbeitungswarteschlange entnommen und die Bearbeitung der aufgerufenen Funktion wurde begonnen.
Mögliche Folgezustände sind: · DONE · ABORT_REQUESTED |
DONE | Die Anfrage wurde vollständig bearbeitet. Wie beim Ausführen der Anfrage aufgetretene Fehler an die aufrufende Anwendung zurückgegeben werden hängt an der Implementierung des Dienstes. Der Zustand DONE ist unabhängig von diesen Fehlern.
Es wurden unter Umständen persistente Daten durch die Anfrage verändert. Dies ist ein Endzustand. |
ABORT_REQUESTED | Die Anfrage im Zustand IN_PROCESS, die aufrufende Anwendung wünscht jedoch den Abbruch der Bearbeitung. Es obliegt der Implementierung des Dienstes diese Abbruchanforderung entweder zu bearbeiten oder begonnene Bearbeitung erfolgreich abzuschließen.
Mögliche Folgezustände sind: · DONE · ABORTED |
ABORTED | Die Anwendung hat den Abbruch Anfragebearbeitung gewünscht.
Es wurden keine persistenten Daten durch die Anfrage verändert. Dies ist ein Endzustand. |
QUEUE_FULL | Die Anfrage konnte nicht verarbeitet werden, da die Warteschlangen der Dienstinstanz voll sind. Voraussichtlich werden mehr Anfragen an die Dienstinstanz gestellt, als diese verarbeiten kann.
Es wurden keine persistenten Daten durch die Anfrage verändert. Dies ist ein Endzustand. |
SVM_NOT_AVAILABLE | Der bei der Anfrage angegebene Application-Server läuft nicht oder es gibt keine Dienstinstanz für den aufgerufenen Dienst.
Es wurden keine persistenten Daten durch die Anfrage verändert. Dies ist ein Endzustand. |
SERVICE_NOT_AVAILABLE | Die Anfrage wurde an einen Application-Server gesenden auf dem keine Dienstinstanz des angegebenen Dienstes läuft.
Es wurden keine persistenten Daten durch die Anfrage verändert. Dies ist ein Endzustand. |
ABORT_TIMEOUT | Die Anwendung wünscht den Abbruch der Anfragebearbeitung. Die Dienstinstanz hat auf diese Abbruchanforderung nicht reagiert.
Es ist unbekannt, ob persistente Daten durch die Anfrage verändert wurden. Dies ist ein Endzustand. |
3.3.2 API zum Funktionsaufruf
Mit der Methode sendRequest des CisServiceManager können Funktionen einer Dienstinstanz aufgerufen werden:
Request sendRequest(Class<? extends CisService> serviceClass,
String serviceId,
int action,
CisParameterList parameters)
Die Parameter serviceClass und serviceId identifizieren den Dienst eindeutig. Der Parameter serviceClass ist die Klasse, die die Dienstschnittstelle implementiert. Die serviceId ist eine beliebige Zeichenkette, die zusätzlich zur Klasse einen Dienst identifiziert. Der Name der OLTP-Datenbank ist häufig Bestandteil der serviceId. Der Parameter action identifiziert die aufgerufene Funktion und der Parameter parameters enthält die Parameter der Funktion. Das Ergebnis des Funktionsaufrufs ist ein Request.
Beispiel:
CisServiceManager srvm = env.getServiceManager();
CisDatabaseManager dbm = env.getDatabaseManager();
// Request absenden
CisServiceManager.Request request =
srvm.sendRequest(
WarehouseManagementService.class,
dbm.getDatabaseName(env.getDatabaseGuid),
WarehouseManagementService.XYZ, parameters);
// Request ausführen
if (!request.waitExecute()) {
mm.sendMessage(request.createStateMessage());
…
}
Optional kann der Methode sendRequest die Guid des Application-Servers mitgegeben werden, in dessen Dienstinstanz die Funktion aufgerufen werden soll.
Der Zustand des Requests kann mit der Methode
short getState()
abgefragt werden. Die Zustände sind Konstanten in der Klasse CisServiceRequestState. Die Methode
boolean isProcessed()
prüft, ob der Request in einem Endzustand (DONE, SVM_NOT_AVAILABLE, SERVICE_NOT_AVAILABLE, ABORTED, ABORT_TIMEOUT) ist. Das Ergebnis kann mit der Methode
CisParameterList getResult()
abfragen werden, Das Ergebnis ist aber nur im Zustand DONE definiert. Mit der Methode
void waitDone()
kann das Anwendungsprogramm warten bis der Request im Zustand DONE ist, oder der Anfragetimeout abgelaufen ist. Mit der Methode
void abort()
wird die Abbruchanforderung versendet und es wird gewartet bis der Abbruchtimout abgelaufen ist.
Die Methode
boolean waitExecute()
ist eine Abkürzung der Sequenz
waitDone();
if (!isProcessed()) {
abort();
}
return getState() == CisServiceRequestState.DONE;
und erleichtert damit den Aufruf von Funktionen. Die Methode
CisMessage createStateMessage()
erzeugt eine CisMessage für den aktuellen (Fehler-)Zustand.
3.3.3 API für Dienste
Ein Dienst besteht normalerweise aus einer Schnittstelle, die die Anfragen an einen oder mehreren Verarbeitungsaufträgen weiterleitet, die die Anfragen dann verarbeiten.
3.3.3.1 Registrierung der Schnittstelle
Die Schnittstelle muss von der abstrakten Basisklasse CisService abgeleitet sein. Beim Start des Verarbeitungsauftrags wird die Verwendung der Schnittstelle am CisServiceManager mit der Methode
<T extends CisService> T register(
Class<T> serviceClass,
String serviceId,
CisServiceRequestProcessor processor);
registriert. Die Parameter serviceClass und serviceId identifizieren den Dienst. Die Klasse serviceClass implementiert die Schnittstelle des Dienstes. Die serviceId ist eine zusätzliche Identifikation der Dienstinstanz. Der Parameter processor identifiziert den Verarbeitungsauftrag. Der Verarbeitungsauftrag muss das Tagging-Interface CisServiceRequestProcessor implementieren.
Das Ergebnis ist die Instanz der Schnittstelle der Klasse serviceClass. Pro Application-Server und Dienst (identifiziert durch serviceClass und serviceId) wird maximal eine Instanz der Klasse serviceClass erzeugt. D.h. wenn mehrere Verarbeitungsaufträge auf einem Application-Server den gleichen Dienst registrieren, dann wird nur eine Instanz der Schnittstelle des Dienstes erzeugt.
Wenn ein Verarbeitungsauftrag keine weiteren Anfragen mehr verarbeitet, dann muss er sich mit der Methode
boolean deregister(
CisService service,
CisServiceRequestProcessor processor);
deregistrieren. Wenn sich der letzte Verwender, identifiziert durch den Parameter processor, der Schnittstelle deregistriert, so wird die Instanz der Schnittstelle verworfen.
3.3.3.2 Aufbau der Schnittstelle
Bei der Implementierung der Schnittstelle kann die Anfrageverarbeitung relativ frei gestaltet werden. Im Folgenden wird der empfohlene Weg der Anfrageverarbeitung beschrieben.
Die Aufgabe der Schnittstelle ist es, Anfragen an die Verarbeitungsaufträge weiter zu leiten. Die Klasse CisService ist die Basisklasse aller Schnittstellen für Dienste.
Jede Anfrage durchläuft die folgenden Schritte:
- Die Anwendung sendet die Anfrage an eine Dienstinstanz
- Die Anfrage wird in die Empfangswartschlange eingereiht.
- Die Anfrage wird aus der Empfangswarteschlange entnommen und einer Verarbeitungswartschlange zugeordnet.
- Die Anfrage wird aus der Verarbeitungswartschlange entnommen und verarbeitet.
- Das Ergebnis der Anfragebearbeitung wird an die Anwendung zurück gesendet.
Der Ablauf der Schritte 1. bis 4. ist in der folgenden Grafik dargestellt:
Beispiel für die Anfrageverarbeitung
Jede Instanz eines CisService wird in einer eigenen Session ausgeführt. Dieser Session ist keine Default-OLTP-Datenbank zugeordnet, so dass Transaktionen auf einer OLTP-Datenbank mit der GUID der OLTP-Datenbank geöffnet werden müssen.
Die Methode receive des CisService ordnet eine Anfrage aus der Emfpangswarteschlange einer Verarbeitungswarteschlange zu. Die Methode abort entfernt eine Anfrage aus einer Verabeitungswarteschlange.
Zur Verwendung der internen Verarbeitungswarteschlange sollten die Methoden receive und abort wie folgt implementiert werden.
public void receive(Request request) {
offer(getInternalQueue(),request);
}
public void abort(Request request) {
remove(getInternalQueue(),request);
}
Die Anfragen der internen Verarbeitungswartschlange werden im CisService in der Methode process verarbeitet.
Da Anfragen in der internen Verarbeitungswarteschlange in der Session verarbeitet werden, in der auch die Methode receive ausgeführt wird, kann, während eine Anfrage in der Methode process verarbeitet wird, keine weitere Anfrage in der Methode receive einer Verarbeitungswartschlange zugeordnet werden. Anfragen mit langer Laufzeit blockieren also die Schnittstelle, wenn dieser innerhalb der Schnittstelle verarbeitet werden. Es sollten daher nur Anfragen mit kurzer Laufzeit über die interne Verarbeitungswartschlange verarbeitet werden. Anfragen mit langer Laufzeit sollten einer anderen Verarbeitungswarteschlange zugeordnet werden. Diese Anfragen werden dann durch die als CisServiceRequestProcessor registrierten Verarbeitungsaufträge verarbeitet.
In vielen Fällen sollen jedoch die Anfragen in Verarbeitungsaufträgen verarbeitet werden. Dazu sollte die Schnittstelle eine weitere Verarbeitungswartschlange bereitstellen. Damit der Status der Anfrage verwaltet werden kann, muss die Anfrage in der Methode receive mit der Methode offer der entsprechenden Verarbeitungswarteschlange zugeordnet werden und in abort mit remove abgebrochen werden.
3.3.3.3 Implementierung der Schnittstelle:
Im folgenden Beispiel werden alle Anfragen einer eigenen Verarbeitungswarteschlange zugeordnet. Die interne Verarbeitungswartschlange wird nicht verwendet.
class WarehouseManagementServiceService extends CisService {
private BlockingQueue<Request> queue;
public WarehouseManagementServiceService() {
queue = new LinkedBlockingQueue<Request>();
}
@Overwrite
public void receive(Request request) {
offer(queue, request);
}
@Overwrite
public void abort(Request request) {
remove(queue, request);
}
public BlockingQueue<Request> getQueue() {
return queue;
}
}
Die Verwendung der Methoden CisService:offer und CisService:remove ist notwendig, damit der Status des Requests richtig gesetzt werden kann.
3.3.3.4 Implementierung des Verarbeitungsauftrags
Die folgende Grafik zeigt den schematischen Ablauf innerhalb des Verarbeitungsauftrags, der die Anfragen eines Dienstes verarbeitet.
Beispiel für die Anfrageverarbeitung in einem Verarbeitungsauftrag
Der Verarbeitungsauftrag registriert als erstes die Verwendung der Schnittstelle des Dienstes. Der Verarbeitungsauftrag läuft solange dieser nicht explizit abgebrochen wird. Damit der Verarbeitungsauftrag abbrechen kann, muss regelmäßig geprüft werden, ob der Abbruch des Verarbeitungsauftrags angefordert worden ist.
Wenn eine Anfrage über die Schnittstelle dem Verarbeitungsauftrag zugeteilt wurde, dann muss vor der Anfrageverarbeitung geprüft werden, ob die Anfrage noch zu verarbeiten ist oder bereits abgebrochen wurde. Während der Anfrageverarbeitung kann optional regelmäßig geprüft werden, ob eine Abbruchanforderung vorliegt. Bei einer Abbruchanforderung steht es dem Verarbeitungsauftrag frei, entweder die Anfrageverarbeitung fortzusetzen oder aber abzubrechen.
Wenn die Anfrageverarbeitung abgeschlossen ist, wird das Ergebnis an der Anfrage gesetzt und auf die nächste Anfrage gewartet.
Beispiel
Beispiel für die Implementierung eines Verarbeitungsauftrags zur Anfrageverarbeitung eines Diensts:
class WarehouseManagementService extends CisBatchApplication
implements CisServiceRequestProcessor {
public void run(int action, parameters) {
WarehouseManagementServiceService service =
svrm.register(WarehouseManagementServiceService.class,
dbm.getDatabaseName(env.getDatabaseGuid()),
this);
try {
while (! env.isToBeRemoved()) {
CisService.Request request =
service.getQueue().poll(TIMEOUT);
if (request==null) {
continue;
}
process(request);
}
} finally {
svrm.deregister(service,this);
}
}
public void process(CisService.Request request) {
// Anfrage in Bearbeitung nehmen und prüfen, ob die Anfrage
// noch zu verarbeiten ist.
if (! request.setState(CisServiceRequestState.IN_PROCESS)) {
return;
}
try {
…
// Optionale Prüfung, ob eine Abbruchanforderung vorliegt
if (request.getState()==
CisServiceRequestState.ABORT_REQUESTED) {
request.setState(CisServiceRequestState.ABORTED);
return;
}
…
} finally {
// Wenn die Anfrage nicht abgebrochen wurde, dann setze
// Anfrageergebnis.
if (! request.isProcessed()) {
request.setResult(result);
request.setState(CisServiceRequestState.DONE);
}
}
}
Das try/finally innerhalb der Methode process muss in jedem Dienst genau so programmiert werden. Die Prüfung auf ABORT_REQUESTED ist jedoch optional. Der Status ABORT darf nur bei einer Abbruchanforderung am Request gesetzt werden. Wenn keine Abbruchanforderung vorliegt, so muss der Status unabhängig davon, ob während der Anfrageverarbeitung Fehler aufgetreten sind, auf DONE gesetzt werden. Der Dienst für die Darstellung von Fehlerzuständen im Anfrageergebnis (z.B. durch einen Fehlerstatus OK, ERROR1, ERROR2, …) selbst verantwortlich.
Mit dem Tool dspsrvins können Sie sich die Dienstinstanzen in Ihrem System anzeigen lassen. Weitere Informationen finden Sie in dem Hilfedokument „Dienstinstanzen anzeigen (dspsrvins)“.