JavaSpaces

Paweł Banasik, Magdalena Dukielska

  1. Wprowadzenie do JavaSpaces
  2. Modele programowania rozproszonego
    1. Programowanie w JavaSpaces
      1. Krotki
      2. Interfejs przestrzeni krotek
    2. Instalacja
      1. Standardowa instalacja
      2. Instalacja w labie
      3. Przydatne wskazówki
    3. Hello Spaces World!
    4. Transakcje Jini
    5. Problemy JavaSpaces
  3. Materiały
  4. Ćwiczenia
    1. 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:

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

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

  1. Instalujemy Jini Technology Starter Kit v2.1. Nie będziemy do tego używali JDK 1.4 jak zalecają w instrukcji instalacji, dowolna wersja 1.5 będzie dobra.
  2. Instalujemy Blitz'a.

Instalacja Blitz'a w labie

  1. 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.
  2. 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).
  3. Instalujemy Blitz'a.
  4. Ustawiamy ścieżkę w blitz.sh do zainstalowanego JRE 1.5.0.

Przydatne wskazówki

Hello Spaces World!

  1. Tworzymy w Eclipse projekt Javy o nazwie HelloSpaces.
  2. We własnościach projektu dodajemy odpowiednie jar'y do classpath (są wymienione w poprzednim punkcie).
  3. 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();
              }
          }
      }
      				
  4. 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 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:

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?

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:

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

  3. 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:

    1. żądają wyłącznie zasobu A i czekają aż będzie dostępny
    2. żądają zasobu A, ale jeśli ten nie jest dostępny, czekają na zasób dowolny
    3. żądają dowolnego zasobu, ale nie czekają, jeśli żaden nie jest dostępny

    Korzystanie z zasobu symulujemy wypisaniem odpowiedniej informacji na konsolę.

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

  5. Napisz program realizujący iteracyjny, rozproszony algorytm obliczania całki oznaczonej ustalonej funkcji f na przedziale [a, 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 [a, c] i [c, b], gdzie c jest środkiem odcinka [a, b]. Jeśli różnica jest większa od zadanej wielkości ε, obliczamy oddzielnie całki na odcinkach [a, c] i [c, b] z dokładnością ε/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: