My favorites | Sign in
Project Logo
                
Search
for
Updated Aug 03, 2008 by mdukielska
JavaSpaces  
A short introduction to JavaSpaces (in Polish).

JavaSpaces

Abstract

This 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

Wprowadzenie do JavaSpaces

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 rozproszonego

Z 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.

Programowanie w JavaSpaces

  • 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.

Krotki

Obiekty, 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:

Interfejs przestrzeni krotek

Instalacja i uruchamianie

Będziemy posługiwać się implementacją JavaSpaces o nazwie Blitz.

Standardowa instalacja Blitz'a

Instalacja Blitz'a w labie

Przydatne wskazówki

Hello Spaces World!

Transakcje Jini

Mechanizm 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:

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

  1. Zaimplementuj procesy Producenta i Konsumenta
  2. 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:

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.

  1. 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:
Korzystanie z zasobu symulujemy wypisaniem odpowiedniej informacji na konsolę.
  1. 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.
  2. 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ązania

Rozwiązania znajdują się tutaj.

Źródła


Comment by nietzold, Aug 07, 2008

can you pls translate this page to english.


Sign in to add a comment
Hosted by Google Code