Wprowadzenie
Usługa udostępnia w ujęciu ogólnym centralne funkcje, z których mogą korzystać aplikacje działające na dowolnych serwerach aplikacyjnych.
Przykład usług:
-
Serwer logistyki magazynowej
-
Serwer planowania
Silnik systemowy zapewnia infrastrukturę dla usług.
Grupa docelowa
-
Programiści
-
Konsultanci techniczni
Opis
Silnik systemowy udostępnia infrastrukturę dla usług. Infrastruktura ta ma następujące właściwości:
-
centralne zarządzanie uruchomionymi usługami
-
wywoływanie usługi bez wskazywania serwera aplikacji
-
obsługa błędów podczas wywołań
-
szczegółowe komunikaty błędów
-
natychmiastowe zwracanie błędów
-
możliwość asynchronicznego wykonywania zapytań
-
większa równoległość
-
lepsze wykorzystanie zasobów
Usługi i instancje usług
Usługi w systemie udostępniają funkcje, z których mogą korzystać dowolne aplikacje. W odróżnieniu od klas logiki, wywoływana funkcja nie jest wykonywana w sesji (wątku) aplikacji wywołującej, lecz w sesji usługi.
Instancja usługi to uruchomiona na serwerze aplikacyjnym usługa. Ta sama usługa może działać na jednym lub wielu serwerach aplikacyjnych, co oznacza, że dla jednej usługi może istnieć wiele instancji. Instancja usługi składa się z interfejsu udostępniającego usługę do obsługi zapytań oraz jednego lub wielu zadań przetwarzających (procesorów), które wykonują zapytania.
Interfejs rozdziela przychodzące zapytania do zadań przetwarzających. Aplikacje komunikują się wyłącznie przez interfejs udostępniany przez usługę. Dla każdej usługi na danym serwerze aplikacyjnym istnieje maksymalnie jedna instancja interfejsu.
Aplikacje mogą wywoływać funkcje instancji usługi także wtedy, gdy same działają na innym serwerze aplikacyjnym.
Aplikacje komunikują się wyłącznie za pośrednictwem interfejsu udostępnianego przez usługę. Dla każdej usługi na danym serwerze aplikacyjnym może istnieć maksymalnie jedna instancja usługi. Aplikacje mogą wywoływać funkcje instancji usług również wtedy, gdy same działają na innym serwerze aplikacji.
Przykład usług w systemie
W przedstawionym przykładzie usługa X działa na serwerze aplikacji 1 oraz 2. Na serwerze aplikacyjnym 1 dwa zadania przetwarzające obsługują zapytania usługi X. Na serwerze aplikacyjnym 2 zapytania usługi X obsługiwane są tylko przez jedno zadanie przetwarzające. Usługa Y działa wyłącznie na serwerze aplikacji1 i posiada jedno zadanie przetwarzające; nie jest uruchomiona na serwerze aplikacyjnym 2.
Informacje o aktualnie uruchomionych instancjach usług są zarządzane centralnie. Dzięki temu funkcje usługi mogą być wywoływane bez konieczności wiedzy, na którym serwerze aplikacyjnym działa dana instancja.
Jeżeli dla danej usługi istnieje więcej niż jedna instancja, wywołania są rozdzielane pomiędzy instancje możliwie równomiernie. Dzięki równomiernemu rozkładowi wywołań usługa skaluje się liniowo wraz ze wzrostem liczby dostępnych instancji usług.
Interfejs usługi
Aplikacje mogą wywoływać funkcje udostępniane przez usługę. Wywołanie funkcji usługi odbywa się z użyciem następujących parametrów:
action (int) – jednoznaczna identyfikacja wywoływanej funkcji; zestaw funkcji zależy od implementacji usługi,
parameters (CisParameterList) – lista parametrów w postaci par klucz–wartość; wymagane parametry zależą od implementacji funkcji.
Instancja usługi wylicza wynik na podstawie action i parameters i zwraca go jako CisParameterList.
Usługa jest identyfikowana przez:
-
klasę implementującą usługę
-
identyfikator Id
(dowolny ciąg znaków)
Do wywołania funkcji usługi potrzebne są:
-
serwer aplikacji (opcjonalnie)
-
klasa usługi
-
Id
-
Action
-
Parameter
Jeżeli dodatkowo zostanie wskazany serwer aplikacji, wywołanie trafi do instancji działającej na tym konkretnym serwerze. Jeżeli serwer nie zostanie podany, wybierana jest dowolna instancja usługi.
W odróżnieniu od wywołania funkcji logiki, przy wywołaniu funkcji usługi mogą wystąpić m.in. następujące błędy:
-
nie uruchomiono żadnej instancji usługi
-
usługa nie przetwarza zapytań
-
serwer aplikacji z instancją usługi został zakończony w trakcie przetwarzania
Wszystkie te sytuacje należy uwzględniać, szczególnie gdy funkcja modyfikuje trwałe dane – w przypadku błędu istotne jest, czy funkcja została wykonana, czy nie.
Interfejs programistyczny
Interfejs programistyczny obejmuje:
-
API do wywoływania funkcji usług przez aplikacje
-
API do tworzenia/usługi (implementacja interfejsu i procesorów)
Statusy wywołań funkcji
Funkcja serwisu może zostać wywołana z dowolnego serwera aplikacyjnego. Wywołanie funkcji serwisu powoduje utworzenie requestu, który zawiera aktualny stan wywołania. Podczas wywołania funkcji obowiązuje następujący przebieg:
Wywołanie funkcji usługi tworzy obiekt Request, który przechowuje aktualny stan wywołania. Po wysłaniu wywołania aplikacja oczekuje, czy instancja usługi przetworzy żądanie w ramach timeoutu zapytania. Po zakończeniu instancja ustawia stan na DONE.
Jeżeli instancja nie zdąży w czasie timeoutu zapytania, aplikacja zwykle żąda przerwania przetwarzania. Jeżeli instancja obsłuży żądanie przerwania, stan przechodzi na ABORTED. Jeżeli jednak instancja zdoła zakończyć przetwarzanie mimo żądania przerwania, stan może przejść na DONE. Jeżeli instancja nie zareaguje na żądanie przerwania w ramach timeoutu przerwania, stan przechodzi na ABORT_TIMEOUT. Timeout przerwania jest zwykle znacznie dłuższy niż timeout zapytania.
Przejścia stanów zlecenia przedstawiono na poniższym schemacie:
Poniżej opis stanów:
SEND – zapytanie wysłane, ale nieodebrane przez żadną instancję.
Kolejne stany: RECEIVED, SVM_NOT_AVAILABLE, SERVICE_NOT_AVAILABLE
RECEIVED – zapytanie odebrane, ale nieprzypisane do kolejki przetwarzania.
Kolejne stany: QUEUE_FULL, ASSIGNED, ABORTED
ASSIGNED – zapytanie przypisane do kolejki przetwarzania.
Kolejne stany: IN_PROCESS, ABORTED
IN_PROCESS – rozpoczęto przetwarzanie funkcji.
Kolejne stany: DONE, ABORT_REQUESTED
DONE – zapytanie przetworzone; ewentualne błędy przekazywane zależnie od implementacji. Stan niezależny od błędów; możliwe zmiany danych trwałych.
Stan końcowy.
ABORT_REQUESTED – zapytanie było w IN_PROCESS, ale aplikacja zażądała przerwania; implementacja może przerwać albo dokończyć.
Kolejne stany: DONE, ABORTED
ABORTED – aplikacja zażądała przerwania; nie zmieniono danych trwałych.
Stan końcowy.
QUEUE_FULL – brak możliwości przetworzenia z powodu przepełnienia kolejek instancji; nie zmieniono danych trwałych.
Stan końcowy.
SVM_NOT_AVAILABLE – wskazany serwer aplikacyjny nie działa albo nie ma instancji usługi; nie zmieniono danych trwałych.
Stan końcowy.
SERVICE_NOT_AVAILABLE – zapytanie wysłano na serwer, na którym nie działa instancja wskazanej usługi; nie zmieniono danych trwałych.
Stan końcowy.
ABORT_TIMEOUT – aplikacja żąda przerwania, ale instancja nie zareagowała w czasie; nie wiadomo, czy dane trwałe zostały zmienione.
Stan końcowy.
API do wywołania funkcji
Funkcje instancji usługi wywołuje się metodą sendRequest klasy CisServiceManager:
Request sendRequest(Class<? extends CisService> serviceClass,
String serviceId,
int action,
CisParameterList parameters)
Parametry serviceClass oraz serviceId jednoznacznie identyfikują serwis. Parametr serviceClass jest klasą, która implementuje interfejs serwisu. serviceId jest dowolnym ciągiem znaków, który wraz z klasą dodatkowo identyfikuje serwis. Nazwa bazy danych OLTP jest często częścią serviceId. Parametr action identyfikuje wywoływaną funkcję, a parametr parameters zawiera parametry funkcji. Wynikiem wywołania funkcji jest request.
CisServiceManager srvm = env.getServiceManager(); CisDatabaseManager dbm = env.getDatabaseManager(); // wysłanie requestu CisServiceManager.Request request = srvm.sendRequest( WarehouseManagementService.class, dbm.getDatabaseName(env.getDatabaseGuid), WarehouseManagementService.XYZ, parameters); // wykonanie requestu if (!request.waitExecute()) { mm.sendMessage(request.createStateMessage()); … }
Opcjonalnie do metody sendRequest można przekazać Guid serwera aplikacji, w którego instancji serwisu ma zostać wywołana funkcja.
Stan requestu można sprawdzić metodą
- short getState()
Stany są stałymi w klasie CisServiceRequestState. Metoda:
- boolean isProcessed()
sprawdza, czy request znajduje się w stanie końcowym (DONE, SVM_NOT_AVAILABLE, SERVICE_NOT_AVAILABLE, ABORTED, ABORT_TIMEOUT). Wynik można pobrać metodą
CisParameterList getResult()
Jednak wynik jest zdefiniowany tylko w stanie DONE. Metodą
- void waitDone()
Program aplikacyjny może czekać, aż request osiągnie stan DONE lub upłynie timeout zapytania. Metoda
- void abort()
wysyła żądanie przerwania i czeka, aż upłynie timeout przerwania.
Metoda:
- boolean waitExecute()
jest skrótem sekwencji
waitDone();
if (!isProcessed()) {
abort();
}
return getState() == CisServiceRequestState.DONE;
waitDone();
if (!isProcessed()) {
abort();
}
return getState() == CisServiceRequestState.DONE;
i tym samym ułatwia wywoływanie funkcji. Metoda:
CisMessage createStateMessage()
tworzy obiekt CisMessage dla aktualnego (błędnego) stanu.
[/example]
API dla usług
Usługa składa się zwykle z interfejsu, który kieruje zapytania do jednego lub wielu zadań przetwarzających.
Rejestracja interfejsu
Interfejs musi dziedziczyć po CisService. Podczas startu zadania przetwarzającego rejestruje się go w CisServiceManager metodą:
<T extends CisService> T register(
Class<T> serviceClass,
String serviceId,
CisServiceRequestProcessor processor);
- serviceClass i serviceId identyfikują usługę
- processor identyfikuje zadanie przetwarzające i musi implementować CisServiceRequestProcessor.
Wynikiem jest instancja interfejsu klasy serviceClass. Na jeden serwer aplikacyjny i serwis (identyfikowany przez serviceClass oraz serviceId) tworzona jest maksymalnie jedna instancja klasy serviceClass. Oznacza to, że jeśli wiele zadań przetwarzania na jednym serwerze aplikacyjnym zarejestruje ten sam serwis, to zostanie utworzona tylko jedna instancja interfejsu serwisu.
Jeżeli zadanie przetwarzania nie obsługuje już żadnych dalszych zapytań, musi się wyrejestrować za pomocą metody
boolean deregister( CisService service, CisServiceRequestProcessor processor);
Jeżeli ostatni użytkownik, identyfikowany przez parametr processor, wyrejestruje się z interfejsu, instancja interfejsu zostanie usunięta.
Struktura interfejsu
Podczas implementacji interfejsu przetwarzanie zapytań może być zaprojektowane w stosunkowo dowolny sposób. Poniżej opisano zalecaną ścieżkę przetwarzania zapytań.
Zadaniem interfejsu jest przekazywanie zapytań do zadań przetwarzających. Klasa CisService jest klasą bazową wszystkich interfejsów dla usług.
Każde zapytanie przechodzi następujące etapy:
Zalecany przepływ obsługi zapytań:
-
aplikacja wysyła zapytanie do instancji usługi
-
zapytanie trafia do kolejki odbiorczej
-
zapytanie jest przypisywane do kolejki przetwarzania
-
zapytanie jest pobierane z kolejki przetwarzania i wykonywane
-
wynik jest odsyłany do aplikacji
Każda instancja CisService działa w osobnej sesji i nie ma domyślnie przypisanej bazy OLTP, więc transakcje na OLTP należy otwierać z użyciem GUID bazy OLTP.
Metody receive i abort służą do przypisywania i usuwania zapytań z kolejki (dla kolejki wewnętrznej):
Przykład implementacji interfejsu
W poniższym przykładzie wszystkie zapytania są przypisywane do własnej kolejki przetwarzania. Wewnętrzna kolejka przetwarzania nie jest wykorzystywana.
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;
}
}
Implementacja zadania przetwarzającego
Poniższy schemat przedstawia schematyczny przepływ w ramach zlecenia przetwarzania, które przetwarza zapytania usługi.
-
rejestruje użycie interfejsu
-
działa w pętli do chwili żądania zakończenia
-
cyklicznie sprawdza, czy wymagane jest przerwanie
-
przed przetwarzaniem sprawdza, czy Request nie został już anulowany
-
opcjonalnie sprawdza podczas przetwarzania, czy istnieje żądanie przerwania (ABORT_REQUESTED)
-
po zakończeniu ustawia wynik i stan
Poniższy przykład przedstawia implementację zlecenia przetwarzania dla obsługi zapytania usługi.
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) {
// Zapytanie jest przyjmowane do przetwarzania i jest sprawdzane, czy zapytanie ma być jeszcze // przetwarzane.
if (! request.setState(CisServiceRequestState.IN_PROCESS)) {
return;
}
try {
…
// Opcjonalne sprawdzenie, czy występuje żądanie anulowania.
if (request.getState()==
CisServiceRequestState.ABORT_REQUESTED) {
request.setState(CisServiceRequestState.ABORTED);
return;
}
…
} finally {
// Jeżeli zapytanie nie zostało anulowane, ustawiany jest wynik zapytania.
if (! request.isProcessed()) {
request.setResult(result);
request.setState(CisServiceRequestState.DONE);
}
}
}
Blok try/finally wewnątrz metody process musi być w każdym serwisie zaimplementowany dokładnie w taki sam sposób. Sprawdzenie ABORT_REQUESTED jest jednak opcjonalne. Status ABORT może być ustawiony wyłącznie w przypadku zgłoszenia żądania przerwania dla requestu. Jeżeli nie ma żądania przerwania, to status – niezależnie od tego, czy podczas przetwarzania żądania wystąpiły błędy – musi zostać ustawiony na DONE. Serwis jest sam odpowiedzialny za prezentację stanów błędów w wyniku żądania (np. poprzez status błędu OK, ERROR1, ERROR2, …).
Narzędzie dspsrvins umożliwia wyświetlenie instancji usług w systemie. Szczegóły znajdują się w artykule Wyświetlenie instancji usług (dspsrvins).








