Wprowadzenie do usług Reliable Services w języku Java
W tym artykule opisano podstawy usług Reliable Services usługi Azure Service Fabric i przedstawiono sposób tworzenia i wdrażania prostej aplikacji Reliable Service napisanej w języku Java.
Instalacja i konfiguracja
Przed rozpoczęciem upewnij się, że masz skonfigurowane środowisko programistyczne usługi Service Fabric na maszynie. Jeśli musisz go skonfigurować, przejdź do tematu Wprowadzenie do komputerów Mac lub wprowadzenie do systemu Linux.
Podstawowe pojęcia
Aby rozpocząć pracę z usługami Reliable Services, musisz zrozumieć tylko kilka podstawowych pojęć:
- Typ usługi: jest to implementacja usługi. Jest ona definiowana przez klasę, którą piszesz, która rozszerza
StatelessService
i dowolny inny kod lub zależności używane w nim, wraz z nazwą i numerem wersji. - Nazwane wystąpienie usługi: aby uruchomić usługę, należy utworzyć nazwane wystąpienia typu usługi, podobnie jak w przypadku tworzenia wystąpień obiektów typu klasy. Wystąpienia usługi są w rzeczywistości wystąpieniami obiektów klasy usługi, którą piszesz.
- Host usługi: tworzone wystąpienia usługi o nazwie muszą być uruchamiane na hoście. Host usługi to tylko proces, w którym można uruchamiać wystąpienia usługi.
- Rejestracja usługi: Rejestracja łączy wszystko razem. Typ usługi musi być zarejestrowany w środowisku uruchomieniowym usługi Service Fabric na hoście usługi, aby umożliwić usłudze Service Fabric tworzenie wystąpień do uruchomienia.
Tworzenie usługi bezstanowej
Zacznij od utworzenia aplikacji usługi Service Fabric. Zestaw SDK usługi Service Fabric dla systemu Linux zawiera generator Yeoman, który udostępnia szkielet dla aplikacji usługi Service Fabric z usługą bezstanową. Zacznij od uruchomienia następującego polecenia narzędzia Yeoman:
$ yo azuresfjava
Postępuj zgodnie z instrukcjami, aby utworzyć niezawodną usługę bezstanową. Na potrzeby tego samouczka nadaj aplikacji nazwę "HelloWorldApplication" i usługę "HelloWorld". Wynik zawiera katalogi dla i HelloWorldApplication
HelloWorld
.
HelloWorldApplication/
├── build.gradle
├── HelloWorld
│ ├── build.gradle
│ └── src
│ └── statelessservice
│ ├── HelloWorldServiceHost.java
│ └── HelloWorldService.java
├── HelloWorldApplication
│ ├── ApplicationManifest.xml
│ └── HelloWorldPkg
│ ├── Code
│ │ ├── entryPoint.sh
│ │ └── _readme.txt
│ ├── Config
│ │ └── _readme.txt
│ ├── Data
│ │ └── _readme.txt
│ └── ServiceManifest.xml
├── install.sh
├── settings.gradle
└── uninstall.sh
Rejestracja usługi
Typy usług muszą być zarejestrowane w środowisku uruchomieniowym usługi Service Fabric. Typ usługi jest zdefiniowany w ServiceManifest.xml
klasie i usługi, która implementuje StatelessService
element . Rejestracja usługi jest wykonywana w głównym punkcie wejścia procesu. W tym przykładzie głównym punktem wejścia procesu jest HelloWorldServiceHost.java
:
public static void main(String[] args) throws Exception {
try {
ServiceRuntime.registerStatelessServiceAsync("HelloWorldType", (context) -> new HelloWorldService(), Duration.ofSeconds(10));
logger.log(Level.INFO, "Registered stateless service type HelloWorldType.");
Thread.sleep(Long.MAX_VALUE);
}
catch (Exception ex) {
logger.log(Level.SEVERE, "Exception in registration:", ex);
throw ex;
}
}
Implementowanie usługi
Otwórz plik HelloWorldApplication/HelloWorld/src/statelessservice/HelloWorldService.java. Ta klasa definiuje typ usługi i może uruchamiać dowolny kod. Interfejs API usługi udostępnia dwa punkty wejścia dla kodu:
- Otwarta metoda punktu wejścia o nazwie
runAsync()
, w której można rozpocząć wykonywanie dowolnych obciążeń, w tym długotrwałych obciążeń obliczeniowych.
@Override
protected CompletableFuture<?> runAsync(CancellationToken cancellationToken) {
...
}
- Punkt wejścia komunikacji, w którym można podłączyć wybrany stos komunikacji. W tym miejscu można rozpocząć odbieranie żądań od użytkowników i innych usług.
@Override
protected List<ServiceInstanceListener> createServiceInstanceListeners() {
...
}
Ten samouczek koncentruje się na metodzie runAsync()
punktu wejścia. W tym miejscu możesz natychmiast rozpocząć uruchamianie kodu.
RunAsync
Platforma wywołuje tę metodę, gdy zostanie umieszczone wystąpienie usługi i będzie gotowe do wykonania. W przypadku usługi bezstanowej oznacza to, że po otwarciu wystąpienia usługi. Token anulowania jest udostępniany do koordynowania, gdy wystąpienie usługi musi zostać zamknięte. W usłudze Service Fabric ten cykl otwierania/zamykania wystąpienia usługi może wystąpić wiele razy w okresie istnienia usługi jako całości. Może się to zdarzyć z różnych powodów, w tym:
- System przenosi wystąpienia usługi na potrzeby równoważenia zasobów.
- Błędy występują w kodzie.
- Aplikacja lub system jest uaktualniany.
- Podstawowa awaria sprzętu.
Ta aranżacja jest zarządzana przez usługę Service Fabric w celu zapewnienia wysokiej dostępności i prawidłowego zrównoważenia usługi.
runAsync()
nie należy blokować synchronicznie. Implementacja polecenia runAsync powinna zwrócić completableFuture, aby umożliwić kontynuowanie działania środowiska uruchomieniowego. Jeśli obciążenie musi zaimplementować długotrwałe zadanie, które powinno zostać wykonane w pliku CompletableFuture.
Anulowanie
Anulowanie obciążenia to nakład pracy współpracy zorganizowany przez podany token anulowania. System czeka na zakończenie zadania (po pomyślnym zakończeniu, anulowaniu lub błędzie), zanim przejdzie dalej. Ważne jest, aby uczcić token anulowania, zakończyć pracę i zakończyć runAsync()
jak najszybciej, gdy system żąda anulowania. W poniższym przykładzie pokazano, jak obsługiwać zdarzenie anulowania:
@Override
protected CompletableFuture<?> runAsync(CancellationToken cancellationToken) {
// TODO: Replace the following sample code with your own logic
// or remove this runAsync override if it's not needed in your service.
return CompletableFuture.runAsync(() -> {
long iterations = 0;
while(true)
{
cancellationToken.throwIfCancellationRequested();
logger.log(Level.INFO, "Working-{0}", ++iterations);
try {
Thread.sleep(1000);
} catch (InterruptedException ex){}
}
});
}
W tym przykładzie usługi bezstanowej liczba jest przechowywana w zmiennej lokalnej. Ponieważ jest to usługa bezstanowa, przechowywana wartość istnieje tylko dla bieżącego cyklu życia wystąpienia usługi. Gdy usługa zostanie przeniesiona lub ponownie uruchomiona, wartość zostanie utracona.
Tworzenie usługi stanowej
Usługa Service Fabric wprowadza nowy rodzaj usługi, która jest stanowa. Usługa stanowa może niezawodnie obsługiwać stan w obrębie samej usługi, co-located z kodem, który go używa. Stan jest wysoce dostępny przez usługę Service Fabric bez konieczności utrwalania stanu w magazynie zewnętrznym.
Aby przekonwertować wartość licznika z bezstanowej na wysoce dostępną i trwałą, nawet jeśli usługa zostanie przeniesiona lub uruchomiona ponownie, potrzebujesz usługi stanowej.
W tym samym katalogu co aplikacja HelloWorld możesz dodać nową usługę yo azuresfjava:AddService
, uruchamiając polecenie . Wybierz pozycję "Reliable Stateful Service" dla swojej struktury i nadaj usłudze nazwę "HelloWorldStateful".
Aplikacja powinna teraz mieć dwie usługi: bezstanową usługę HelloWorld i stanową usługę HelloWorldStateful.
Usługa stanowa ma te same punkty wejścia co usługa bezstanowa. Główną różnicą jest dostępność dostawcy stanu, który może niezawodnie przechowywać stan. Usługa Service Fabric jest dostarczana z implementacją dostawcy stanu o nazwie Reliable Collections, która umożliwia tworzenie zreplikowanych struktur danych za pomocą menedżera niezawodnego stanu. Stanowa usługa Reliable Service domyślnie używa tego dostawcy stanu.
Otwórz HelloWorldStateful.java w funkcji HelloWorldStateful —> src, która zawiera następującą metodę RunAsync:
@Override
protected CompletableFuture<?> runAsync(CancellationToken cancellationToken) {
Transaction tx = stateManager.createTransaction();
return this.stateManager.<String, Long>getOrAddReliableHashMapAsync("myHashMap").thenCompose((map) -> {
return map.computeAsync(tx, "counter", (k, v) -> {
if (v == null)
return 1L;
else
return ++v;
}, Duration.ofSeconds(4), cancellationToken)
.thenCompose((r) -> tx.commitAsync())
.whenComplete((r, e) -> {
try {
tx.close();
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage());
}
});
});
}
RunAsync
RunAsync()
działa podobnie w usługach stanowych i bezstanowych. Jednak w usłudze stanowej platforma wykonuje dodatkową pracę w Twoim imieniu przed wykonaniem RunAsync()
polecenia . Ta praca może obejmować zapewnienie gotowości niezawodnych menedżerów stanów i niezawodnych kolekcji do użycia.
Reliable Collections and the Reliable State Manager
ReliableHashMap<String,Long> map = this.stateManager.<String, Long>getOrAddReliableHashMapAsync("myHashMap")
ReliableHashMap to implementacja słownika, której można użyć do niezawodnego przechowywania stanu w usłudze. Za pomocą usług Service Fabric i Reliable HashMap można przechowywać dane bezpośrednio w usłudze bez konieczności zewnętrznego magazynu trwałego. Reliable HashMaps udostępniają dane o wysokiej dostępności. Usługa Service Fabric umożliwia utworzenie wielu replik usługi i zarządzanie nimi. Udostępnia również interfejs API, który oddziela złożoność zarządzania tymi replikami i ich przejściami stanu.
Kolekcje Reliable Mogą przechowywać dowolny typ języka Java, w tym typy niestandardowe, z kilkoma zastrzeżeniami:
Usługa Service Fabric udostępnia stan wysokiej dostępności przez replikowanie stanu między węzłami, a funkcja Reliable HashMap przechowuje dane na dysku lokalnym na każdej repliki. Oznacza to, że wszystkie elementy przechowywane w elementach Reliable HashMaps muszą być serializowalne.
Obiekty są replikowane w celu zapewnienia wysokiej dostępności podczas zatwierdzania transakcji na niezawodnych mapach skrótów. Obiekty przechowywane w elementach Reliable HashMaps są przechowywane w pamięci lokalnej w usłudze. Oznacza to, że masz lokalne odwołanie do obiektu.
Ważne jest, aby nie mutować lokalnych wystąpień tych obiektów bez wykonywania operacji aktualizacji na niezawodnej kolekcji w transakcji. Dzieje się tak, ponieważ zmiany w lokalnych wystąpieniach obiektów nie będą replikowane automatycznie. Należy ponownie włączyć obiekt do słownika lub użyć jednej z metod aktualizacji w słowniku.
Narzędzie Reliable State Manager zarządza niezawodnymi mapami skrótów. Możesz poprosić Menedżera niezawodnego stanu o niezawodną kolekcję według nazwy w dowolnym momencie i w dowolnym miejscu w usłudze. Niezawodny menedżer stanu gwarantuje, że otrzymasz odwołanie z powrotem. Nie zalecamy zapisywania odwołań do niezawodnych wystąpień kolekcji w zmiennych lub właściwościach składowych klasy. Należy zachować szczególną ostrożność, aby zapewnić, że odwołanie jest ustawione na wystąpienie przez cały czas w cyklu życia usługi. Niezawodny menedżer stanu obsługuje tę pracę dla Ciebie i jest zoptymalizowany pod kątem powtarzanych wizyt.
Operacje transakcyjne i asynchroniczne
return map.computeAsync(tx, "counter", (k, v) -> {
if (v == null)
return 1L;
else
return ++v;
}, Duration.ofSeconds(4), cancellationToken)
.thenCompose((r) -> tx.commitAsync())
.whenComplete((r, e) -> {
try {
tx.close();
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage());
}
});
Operacje na niezawodnych mapach skrótów są asynchroniczne. Jest to spowodowane tym, że operacje zapisu w elementach Reliable Collections wykonują operacje we/wy w celu replikowania i utrwalania danych na dysku.
Niezawodne operacje hashMap są transakcyjne, dzięki czemu można zachować spójność stanu na wielu niezawodnych mapach skrótów i operacjach. Możesz na przykład pobrać element roboczy z jednego słownika Reliable Dictionary, wykonać na nim operację i zapisać wynik w innym elemencie Reliable HashMap, a wszystko to w ramach jednej transakcji. Jest to traktowane jako operacja niepodzielna i gwarantuje, że cała operacja powiedzie się lub cała operacja zostanie wycofana. Jeśli po dequeue elementu wystąpi błąd, ale przed zapisaniem wyniku, cała transakcja zostanie wycofana, a element pozostanie w kolejce do przetworzenia.
Kompilowanie aplikacji
Tworzenie szkieletu narzędzia Yeoman obejmuje skrypt narzędzia gradle umożliwiający skompilowanie aplikacji i skryptów powłoki bash w celu wdrożenia i usunięcia aplikacji. Aby uruchomić aplikację, najpierw skompiluj aplikację przy użyciu narzędzia gradle:
$ gradle
Spowoduje to utworzenie pakietu aplikacji usługi Service Fabric, który można wdrożyć przy użyciu interfejsu wiersza polecenia usługi Service Fabric.
Wdrażanie aplikacji
Skompilowaną aplikację można wdrożyć w klastrze lokalnym.
Połącz się z lokalnym klastrem usługi Service Fabric.
sfctl cluster select --endpoint http://localhost:19080
Uruchom skrypt instalacji udostępniony w szablonie, aby skopiować pakiet aplikacji do magazynu obrazów klastra, zarejestrować typ aplikacji i utworzyć wystąpienie aplikacji.
./install.sh
Wdrażanie skompilowanej aplikacji przebiega tak samo jak w przypadku innych aplikacji usługi Service Fabric. Szczegółowe instrukcje są dostępne w dokumentacji dotyczącej zarządzania aplikacją usługi Service Fabric za pomocą interfejsu wiersza polecenia usługi Service Fabric.
Parametry tych poleceń można znaleźć w manifestach wygenerowanych w pakiecie aplikacji.
Po wdrożeniu aplikacji otwórz przeglądarkę i przejdź do narzędzia Service Fabric Explorer pod adresem http://localhost:19080/Explorer
. Następnie rozwiń węzeł Aplikacje i zwróć uwagę, że istnieje teraz wpis dla danego typu aplikacji i inny wpis dla pierwszego wystąpienia tego typu.
Ważne
Aby wdrożyć aplikację w bezpiecznym klastrze systemu Linux na platformie Azure, należy skonfigurować certyfikat w celu zweryfikowania aplikacji przy użyciu środowiska uruchomieniowego usługi Service Fabric. Dzięki temu usługi Reliable Services mogą komunikować się z podstawowymi interfejsami API środowiska uruchomieniowego usługi Service Fabric. Aby dowiedzieć się więcej, zobacz Konfigurowanie aplikacji reliable services do uruchamiania w klastrach systemu Linux.