AbstractThis document was created almost a year ago, when JavaSpaces Netbeans project was starting. It presents the basics of JavaSpaces technology together with its pros and cons. I show how to install all libraries and run a JavaSpace application without the help of an IDE. Spis treści- Wprowadzenie do JavaSpaces
- Modele programowania rozproszonego
- Programowanie w JavaSpaces
- Krotki
- Interfejs przestrzeni krotek
- Instalacja
- Standardowa instalacja
- Instalacja w labie
- Przydatne wskazówki
- Hello Spaces World!
- Transakcje Jini
- Problemy JavaSpaces
- Materiały
- Ćwiczenia
- Rozwiązania
JavaSpaces to interfejs do programowania rozproszonego oparty na modelu Lindy (wzorowane na Linda-C, ale zoptymalizowane dla Javy). Działa on w środowisku Jini w JVM. Jini to standard Sun'a dla urządzeń działających przez sieć podobny do "Plug-and-Play". Mimo że interfejs JavaSpaces zawiera niewiele prostych metod, pozwala on na współpracę nawet bardzo różniących się od siebie komputerów dzięki udostępnianiu operacji na przestrzeniach krotek, rozproszonych zdarzeń, transakcji i dzierżaw (leases). Dzięki temu łatwo jest w JavaSpaces tworzyć skalowalne systemy do rozproszonego przetwarzania danych (m. in. automatycznie uzyskujemy równoważenie obciążenia). Modele programowania rozproszonegoZ programowaniem rozproszonym związanych jest wiele problemów: współpracujące procesy muszą się komunikować i synchronizować, trzeba sobie radzić z opóźnieniami spowodowanymi komunikacją przez sieć, częściowymi porażkami (partial failure) i niekompatybilnymi językami programowania. Najbardziej znane implementacje pozwalające na programowanie rozproszone to: Remote Procedure Calls (RPCs), Distributed Component Object Model (DCOM), Common Object Request Broker Architecture (CORBA), Remote Method Invocation (RMI). Niestety te podejścia mają wady, np. głównym problemem w rozwiązaniach typu RPC jest to, że klient i serwer muszą bardzo dużo o sobie wiedzieć, żeby dobrze współdziałać. W wyniku tego są one ze sobą ściśle związane (tightly coupled). Zmiana w jednym wymusza zmiany w drugim. W przeciwieństwie do poprzednio wymienionych technologii JavaSpaces zapewnia jednolity mechanizm komunikacji, synchronizacji i wymiany danych, działając jako pośrednik między procesami oferującymi usługi i tymi, które z nich korzystają. JavaSpaces jest więc minimalistycznym modelem zupełnie zastępującym RPC. Jednocześnie większość współbieżnych czy rozproszonych problemów można łatwo rozwiązać za pomocą tej technologii. Przestrzeń krotek, na której opiera się JavaSpaces to rozproszona, wirtualna, dzielona pamięć. API składa się praktycznie z 4 metod: write (zapisanie krotki w przestrzeni), read (odczytanie krotki, stworzenie jej kopii), take (zabranie krotki z przestrzeni) i notify (informowanie o zmianach w przestrzeni krotek). W JavaSpaces w przeciwieństwie do typowej pamięci dzielonej procesy nie mogą bezpośrednio modyfikować krotek, kiedy są one w przestrzeni - żeby zmodyfikować obiekt proces musi go usunąć z przestrzeni, uaktualnić i włożyć z powrotem. Trudno byłoby wymyślić prostszy schemat rozproszonego systemu. W RPC procesy komunikują się i synchronizują przez bezpośrednie wywoływanie swoich metod. To podejście nie skaluje się dobrze bez dodatkowego wysiłku - im więcej procesów, tym więcej czasu zajmuje i jest bardziej skomplikowane. W JavaSpaces to przestrzeń krotek odpowiada za synchronizację procesów i dzięki temu rozbudowa systemu sprowadza się do uruchomienia kolejnych procesów.
Komunikujące się obiekty: - Nie muszą o sobie wiedzieć (connectionless anonymous data exchange).
- Nie muszą być w tym samym procesie ani na tej samej maszynie (interprocess).
- Nie muszą istnieć w tym samym czasie (asynchronous).
Czyli w JavaSpaces uzyskujemy wszystkie 3 stopnie swobody pokazane na rysunku, a to z kolei prowadzi do dobrych i elastycznych systemów.
- Przestrzeń krotek jest współdzielona: wiele procesów równolegle z niej korzysta i to ona zajmuje się ich synchronizacją.
- Przestrzeń krotek jest trwała: umieszczony w przestrzeni krotek obiekt zostanie tam dopóki jakiś proces go nie usunie. Są dwa typy przestrzeni krotek: transient - krotki są usuwane przy restarcie systemu i persistent - krotki są zapisywane przy zamykaniu systemu i odtwarzane po restarcie. Można też "wydzierżawić" (lease) czas, przez który obiekt będzie trzymany (jest m. in. możliwość przedłużania tego czasu).
- Przestrzenie krotek działają jak pamięć asocjacyjna: krotki są odnajdywane na podstawie ich zawartości, a nie np. położenia w pamięci czy sztucznych identyfikatorów. Aby odnaleźć krotkę musimy utworzyć wzorzec (obiekt z interesującymi nas polami ustawionymi na konkretne wartości a z pozostałymi równymi null). Taki wzorzec przekazujemy jako jeden z parametrów operacji take czy read. Obiekt z przestrzeni krotek będzie pasował do naszego wzorca, jeśli wartości jego pól będą dokładnie równe ustawionym przez nas wartościom we wzorcu, wartości pól równych null we wzorcu w pasującym obiekcie mogą być dowolne.
Co więcej wspierane jest dopasowywanie obiektów podklas do wzorca z nadklasy - przy dopasowywaniu istotne są tylko wartości pól obecnych w nadklasie (wzorzec pasuje do każdego obiektu ze swojej podklasy). Dzięki temu rozszerzanie serwisu korzystającego z JavaSpaces wymaga tylko dopisania kolejnych podklas - istniejące aplikacje będą działały poprawnie korzystając ze starych interfejsów, a w razie potrzeby nowe definicje klas zostaną automatycznie ściągnięte. - Przestrzenie krotek są chronione transakcjami: domyślnie każda operacja na przestrzeni krotek jest atomowa. Transakcje są wspierane przy operacjach na pojedynczej lub wielu przestrzeniach krotek. Używa się ich bardzo często, aby radzić sobie z problemem częściowej porażki (partial failure).
- W przestrzeni można umieszczać obiekty z metodami: dopóki obiekt jest w przestrzeni nie wolno go modyfikować ani wywoływać jego metod. Ale kiedy go przeczytamy albo pobierzemy tworzona jest lokalna kopia, którą można posługiwać się tak samo jak zwykłym obiektem. W ten sposób łatwo jest stworzyć tzw. serwer obliczeniowy - zarządca umieszcza w przestrzeni krotki ze zleceniami obliczeń - zwykle są one realizacją wzorca projektowego Command - a robotnicy pobierają zlecenia i wykonują je. Kod robotnika jest niezależny od konkretnego zadania, które oblicza. Jednocześnie, jeśli utworzymy różne podklasy klasy Command, ci sami robotnicy będą wykonywać różne prace.
KrotkiObiekty, które umieszczamy w przestrzeni krotek muszą implementować interfejs Entry. Entry jest to tzw. marker interface - nie zawiera żadnych metod, które musielibyśmy implementować, jedyne, co potrzebujemy to dodać implements Entry do definicji klasy. Konwencje, jakie muszą spełniać krotki: - bezargumentowy konstruktor - wynika to z serializacji, która ma miejsce przy przekazywaniu krotek do/z przestrzeni
- publiczne pola - dzieki temu inne procesy mogą je sprawnie wyszukiwać
- pola mogą zawierać tylko referencje do obiektów - nie może być podstawowych typów (np. zamiast int używamy Integer)
Interfejs przestrzeni krotek- write(Entry entry, Transaction txn, long lease): - Umieszcza kopię krotki w przestrzeni. Jeśli wywołamy tę metodę wielokrotnie dla tej samej krotki, w przestrzeni znajdzie się jej wiele kopii. Jako wynik tej metodu otrzymamy obiekt klasy Lease, który zawiera informacje jak długo nasza krotka będzie trzymana w przestrzeni (w szczególności ten czas może być krótszy niż sobie życzyliśmy).
- read(Entry tmpl, Transaction txn, long timeout): - Zwraca kopię krotki pasującej do wzorca. Jeśli takiej nie ma to czekamy określony czas.
- take(Entry tmpl, Transaction txn, long timeout): - To samo co read tylko krotka jest usuwana z przestrzeni.
- notify(Entry tmpl, Transaction txn, RemoteEventListener listener, long lease, MarshalledObject handback): - Po wywołaniu tej metody przestrzeń krotek będzie powiadamiała obiekt, kiedy w przestrzeni będą pojawiać się krotki pasujące do wskazanego wzorca.
- snapshot(Entry e): - Metoda do zmniejszania kosztów serializacji, która ma miejsce za każdym razem krotki albo wzorce są używane.
Instalacja i uruchamianieBędziemy posługiwać się implementacją JavaSpaces o nazwie Blitz. Standardowa instalacja Blitz'aInstalacja Blitz'a w labie- Niestety instalator Jini nie działa w labie, ale możemy wybrać na stronie dla Jini Technology Starter Kit v2.1 wersję zip, którą wystarczy rozpakować w jakimś katalogu.
- Blitz nie działa na Javie 6.0, która jest w labie, więc ściągamy i instalujemy JRE 5.0 (ta java będzie używana tylko do uruchamiania Blitz'a - do kompilacji naszego kodu możemy używać wersji 6.0).
- Instalujemy Blitz'a.
- Ustawiamy ścieżkę w blitz.sh do zainstalowanego JRE 1.5.0.
Przydatne wskazówki- Blitz'a uruchamia się skryptem blitz.sh (lub blitz.bat pod Windowsem). Jeśli coś nie działa, to sprawdzamy, czy w tym pliku wszystkie podane ścieżki są poprawne. Jeśli to nie pomoże, sprawdzamy ustawienia wpisane w pliku blitz/config/start-blitz.config
- Kod możemy edytować np. w Eclipse, ale musimy dołączyć do projektu biblioteki:
- jini2_1/lib/jini-core.jar
- jini2_1/lib/jini-ext.jar
- jini2_1/lib/reggie.jar
- blitz/lib/blitz.jar
- blitz/lib/blitz-dl.jar
- blitz/lib/blitzui.jar
- Przy uruchamianiu w Run As...->Arguments->VM arguments wpisujemy -Djava.security.policy=policy.all. Do tego potrzebujemy w głównym katalogu projektu pliku policy.all o następującej treści:
grant {
permission java.security.AllPermission;
}; Aby uzyskać dostęp do przestrzeni krotek najłatwiej jest użyć klasy Lookup w następujący sposób: Lookup finder = new Lookup(JavaSpace.class);
JavaSpace space = (JavaSpace) finder.getService(); Do czyszczenia przestrzeni krotek można użyć klasy Clean (czasem nieprawidłowo działający program zostawia jakieś śmiecie, które zostaną w przestrzeni krotek na zawsze, jeśli ich nie usuniemy, a to może popsuć działanie innych programów korzystających z tej samej przestrzeni). Hello Spaces World!- Tworzymy w Eclipse projekt Javy o nazwie HelloSpaces.
- We własnościach projektu dodajemy odpowiednie jar'y do classpath (są wymienione w poprzednim punkcie).
- Tworzymy pakiet hello a w nim klasy:
- Lookup - do uzyskania dostępu do przestrzeni krotek
- Message - klasa reprezentująca pojedyncza krotkę wstawianą do przestrzeni o treści:
package hello;
import net.jini.core.entry.Entry;
public class Message implements Entry {
public String content;
// a no-arg constructor
public Message() {
}
}HelloWorld - klasa testująca działanie JavaSpaces o treści: package hello;
import net.jini.core.lease.Lease;
import net.jini.space.JavaSpace;
public class HelloWorld {
public static void main(String[] args) {
try {
Message msg = new Message();
msg.content = "Hello Spaces World!";
Lookup finder = new Lookup(JavaSpace.class);
JavaSpace space = (JavaSpace) finder.getService();
space.write(msg, null, Lease.FOREVER);
Message template = new Message();
Message result =
(Message)space.read(template, null, Long.MAX_VALUE);
System.out.println(result.content);
} catch (Exception e) {
e.printStackTrace();
}
}
}Teraz możemy uruchomić nasz projekt: - uruchamiamy Blitz'a skryptem blitz.bat
- tworzymy w katalogu projektu plik policy.all
- w Run As... tworzymy nową konfigurację dla Java Application i w Arguments -> VM arguments wpisujemy -Djava.security.policy=policy.all
- zapisujemy konfigurację i uruchamiamy :)
Transakcje JiniMechanizm transakcji Jini pozwala na zarządzanie grupą uczestników transakcji i przeprowadzenie two-phase commit protocol (zarządcą transakcji jest TransactionManager, a uczestnik transakcji implementuje interfejs TransactionParticipant). Zapewnione są własności ACID dla rozproszonych transakcji: - Atomowość (atomicity) - z perspektywy klienta albo wszystkie operacje w transakcji zostaną wykonane albo żadna.
- Spójność (consistency) - transakcja zakończona sukcesem pozostawia system w spójnym stanie, jeśli nie da się tego osiągnąć wszystkie zmiany są cofane.
- Izolacja (isolation) - jednocześnie wykonujące się transakcje nie wpływają na siebie. Uczestnicy transakcji widzą pośrednie stany tylko swojej transakcji.
- Trwałość (durability) - obiekty powstałe w wyniku transakcji są trwałe (z dokładnością do mechanizmu dzierżaw).
Jini zapewnia tylko interfejsy pozwalające na operowanie na transakcjach, a reszta zależy od konkretnej implementacji. Transakcje dzielimy na dwa typy: - Płaskie - mogą się wykonywać na wielu serwerach i wielu platformach. Są dostępne w implementacji transakcji, którą dostajemy razem z Jini (mahalo).
- Zagnieżdżone - tworzą drzewiastą strukturę, transakcje w liściach są zatwierdzane dopiero jeśli commituje się ich rodziców (nie ma ich w mahalo).
Korzystanie z takich transakcji za pomocą JavaSpaces jest stosunkowo proste: tworzymy nową transakcję za pomocą klasy TransactionFactory - uzyskana w ten sposób transakcja będzie ważna przez pewien ustalony przedział czasu podany przez użytkownika (jeśli transakcja nie zostanie zacommitowana przed upływem tego czasu, to będzie automatycznie abortowana - dzięki temu nie musimy się przejmować sytuacjami, kiedy proces działający na przestrzeni krotek przez sieć zostanie nagle odłączony). Utworzoną transakcję przekazujemy jako argument do każdej operacji na krotkach, która ma uczestniczyć w transakcji, a na końcu wywołujemy metodę commit. Jeśli po drodze będą jakieś problemy, które wywołają abort, to przestrzeń krotek będzie w takim stanie, jak przed nieudaną traksakcją. Jeśli wstawimy krotkę do przestrzeni, używając transakcji, będzie ona widoczna tylko dla procesów biorących udział w tej transakcji - nikt z zewnątrz jej nie zobaczy dopóki transakcja nie będzie zacommitowana. Jeśli transakcja się nie uda, to utworzone w niej krotki zostaną wyrzucone, a krotki pobrane w ramach tej transakcji zostaną zwrócone do przestrzeni. private void createMessages() {
for (int i = 0; i < numMessages; i++) {
Message msg = new Message();
msg.content = "" + i;
try {
sourceSpace.write(msg, null, Lease.FOREVER);
System.out.println("Wrote message " + i + " to " + sourceName);
} catch (Exception e) {
System.err.println("Cant write message " + i + ": " + e);
}
}
}
private void relayMessages() {
TransactionManager mgr = TransactionManagerAccessor.getManager();
Message template = new Message();
Message msg = null;
for (int i = 0; i < numMessages; i++) {
Transaction.Created trc = null;
try {
trc = TransactionFactory.create(mgr, 300000);
} catch (Exception e) {
System.err.println("Could not create transaction " + e);
return;
}
Transaction txn = trc.transaction;
try {
try {
template.content = "" + i;
// take message under a transaction
msg = (Message)sourceSpace.take(template, txn, Long.MAX_VALUE);
System.out.println("Took msg " + i + " out of " + sourceName);
// write message to the other spaces under a transaction
for (int j = 0; j < targetSpaces.length; j++) {
targetSpaces[j].write(msg, txn, Lease.FOREVER);
System.out.println("Wrote message " + i + " to " + targetNames[j]);
}
} catch (Exception e) {
System.err.println("Can't relay message " + i + ": " + e);
txn.abort();
return;
}
txn.commit();
} catch (Exception e) {
System.err.println("Transaction failed");
return;
}
}
}Klasa TransactionManagerAccessor nie jest częścią JavaSpaces, ale znacznie ułatwia korzystanie z transakcji bez znajomości szczegółów Jini. Problemy - dlaczego JavaSpaces nie jest popularne?- Trudna konfiguracja środowiska. Szczegóły w The Nuts and Bolts of Compiling and Running JavaSpaces Programs. Dodatkowo implementacja, którą dostajemy razem z Jini (Outrigger Suna) nie jest ani zbyt dobra ani dobrze udokumentowana, dlatego lepiej od razu ściągnąć inną implementację, np. Blitz albo GigaSpaces (linki w materiałach).
- Związanie JavaSpaces z Jini. Jini umożliwia różnym rodzajom urządzeń - od komputerów do tosterów - komunikację przez sieć. Choć ta technologia jest na rynku już kilka lat, to nie jest powszechnie stosowana. Jini wymaga JVM, przez co potrzebuje dużo więcej pamięci niż znajduje się w prostych urządzeniach. Dodatkowo konkurentem Jini jest Universal Plug-and-Play Microsoftu, które jest znacznie prostsze i często stosowane.
- Mało osób zna dobrze JavaSpaces - większość programistów słyszało o JavaSpaces, ale nie używało ich na tyle, żeby dobrze rozumieć jak działają albo co można za ich pomocą uzyskać.
- JavaSpaces nie jest powiązane z bazami danych (jak np. konkurencyjne TSpaces IBMu).
Materiały Ćwiczenia- Zaimplementuj procesy Producenta i Konsumenta
- Zasymuluj za pomocą przestrzeni krotek mechanizm zdalnego wywoływania procedur. Serwer ma otrzymywać informację o nazwie metody, którą ma wykonać i tablicę jej parametrów, po czym wykonywać metodę i przesyłać wynik do Klienta. Zaimplementuj następujące metody w serwerze:
- Integer iloczyn(Integer[] czynniki)
- String konkatenacja(String[] napisy)
Klienci korzystają z powyższych metod, wywołując je dla przykładowych danych i wypisując otrzymane wyniki. Można przyjąć, że tablica parametrów metody jest klasy Object[] - Serwer może wtedy założyć, że jej elementy są typu odpowiedniego dla wywoływanej metody. - Stwórz symulację systemu, w którym znajdują się dwa typy zasobów: A i B. Istnieje M egzemplarzy zasobu A oraz N (N > M) egzemplarzy zasobu B. Ponadto w systemie działają trzy grupy procesów:
- żądają wyłącznie zasobu A i czekają aż będzie dostępny
- żądają zasobu A, ale jeśli ten nie jest dostępny, czekają na zasób dowolny
- żądają dowolnego zasobu, ale nie czekają, jeśli żaden nie jest dostępny
Korzystanie z zasobu symulujemy wypisaniem odpowiedniej informacji na konsolę. - Zaimplementuj rozproszone mnożenie macierzy ustalonej wielkości. Główny proces inicjalizuje przestrzeń krotek i wypisuje obliczone elementy macierzy. Procesy robocze działają w pętli. W każdym jej obrocie wyliczają wartość jednego elementu macierzy wynikowej. Zadbaj o to, aby wszystkie procesy kończyły się po zakończeniu obliczenia.
- Napisz program realizujący iteracyjny, rozproszony algorytm obliczania całki oznaczonej ustalonej funkcji f na przedziale b metodą trapezów. Kolejny krok iteracji polega na obliczeniu pola trapezu wyznaczonego przez punkty: a, b, f(a), f(b) i porównaniu go z sumą pól trapezów zbudowanych na odcinkach c i b, gdzie c jest środkiem odcinka b. Jeśli różnica jest większa od zadanej wielkości ε, obliczamy oddzielnie całki na odcinkach c i b z dokładnością epsilon/2 każdą i sumujemy je. Obliczenie tych całek powinno odbywać się współbieżnie. Cały proces powinien być realizowany przez pewną liczbę identycznych wykonawców pobierających zlecenia z przestrzeni krotek i tam umieszczających zarówno kolejne zlecenia, jak też wyniki.
RozwiązaniaRozwiązania znajdują się tutaj. Źródła- M. Ben-Ari - "Podstawy programowania współbieżnego i rozproszonego"
- Scenariusze ćwiczeń z Programowania Współbieżnego p. M. Engela
|
can you pls translate this page to english.