1  Parallélisme

Eric Goubault (et Matthieu Martel)

Commissariat à l'Energie Atomique

Saclay

2  Remote Method Invocation

Références: JAVA, Network Programming and Distributed Computing, D. Reilly et M. Reilly, Addison-Wesley.

et http://java.sun.com/products/jdk/rmi/

3  Architecture

4  Les classes implémentant Serializable

5  Les classes remote

6  Exemple - on retourne un Serializable

``Hello World'' distribué: on va construire les classes:


7  Interface de l'objet distant

import java.rmi.*;

public interface HelloInterface extends Remote {
    public String say() throws RemoteException;
}

8  Hello World: implémentation de l'objet distant

import java.rmi.*;
import java.rmi.server.*;

public class Hello extends UnicastRemoteObject 
  implements HelloInterface {
  private String message;
  
  public Hello(String msg) throws RemoteException {
    message = msg; }

9  Hello World: implémentation de l'objet distant

  public String say() throws RemoteException {
    return message;
  }
}

10  Compilation

javac HelloInterface.java
javac Hello.java
(crée HelloInterface.class et Hello.class)

Création des stubs et squelettes:

rmic Hello
(crée Hello_Stub.class et Hello_Skel.class)

11  Client

  import java.rmi.*;
  public class HelloClient {
    public static void main(String[] argv) {
      try {
        HelloInterface hello = 
        (HelloInterface) Naming.lookup
                ("//cher.polytechnique.fr/Hello");
        System.out.println(hello.say());
(le serveur est supposé toujours être sur cher, voir plus loin pour d'autres méthodes)

12  Client

      } catch(Exception e) {
          System.out.println("HelloClient exception: "+e);
      }
    }
  }

13  Serveur

  import java.rmi.*;
  
  public class HelloServer {
    public static void main(String[] argv) {
      try {
        Naming.rebind("Hello",new Hello("Hello, world!"));
        System.out.println("Hello Server is ready.");

14  Serveur

      } catch(Exception e) {
          System.out.println("Hello Server failed: "+e);
      }
    }
  }

15  Compilation et démarrage du serveur

Faire attention au CLASSPATH (doit au moins contenir . et/ou les répertoires contenant les .class nécessaires, accessibles de toutes les machines sous NFS)

javac HelloClient.java
javac HelloServer.java
Démarrer le serveur de noms:

rmiregistry &
(attendre un minimum)

16  Compilation et démarrage du serveur

Démarrer le serveur (Hello):

java HelloServer &
(attendre un peu)

17  Démarrage des clients et exécution

(ici en local)

> Hello Server is ready.
> java HelloClient
Hello, world!

18  Installation locale aux salles de TD

19  Execution en salle TD

(récupérer les programmes java dans ~goubaul1/Cours03/RMI/*)

Exemple: serveur sur cher,

[goubaul1@cher HelloNormal]$ java HelloServer
Hello Server is ready.
Client sur loire,
[goubaul1@loire HelloNormal]$ java HelloClient
Hello, world!

20  Callback

L'idée est la suivante (programmation ``événementielle'', typique d'interfaces graphique par exemple AWT):

21  Principe du ``rappel''

Comment notifier un objet (distant) de l'apparition d'un événement? Ainsi, Cela implique que ``clients'' et ``serveurs'' sont tous à leur tour ``serveurs'' et ``clients''.

22  C'est à dire...


23  Exemple - interface associée à un évévenement

... ici, changement de température:

interface TemperatureListener extends java.rmi.Remote {
    public void temperatureChanged(double temperature)
         throws java.rmi.RemoteException;
}
C'est la méthode de notification de tout client intéressé par cet événement. Forcément un objet Remote.

24  Exemple - l'interface du serveur d'événements

... doit au moins pouvoir permettre l'inscription et la désinscription de clients voulant être notifié:

interface TemperatureSensor extends java.rmi.Remote {
    public double getTemperature() throws
        java.rmi.RemoteException;
    public void addTemperatureListener
        (TemperatureListener listener)
        throws java.rmi.RemoteException;
    public void removeTemperatureListener
        (TemperatureListener listener)
        throws java.rmi.RemoteException; }

25  Exemple - l'implémentation du serveur

26  Exemple - implémentation serveur

import java.util.*;
import java.rmi.*;
import java.rmi.server.*;

public class TemperatureSensorServer 
    extends UnicastRemoteObject
    implements TemperatureSensor, Runnable {
    private volatile double temp;
    private Vector list = new Vector();
(le vecteur list contiendra la liste des clients)

27  Exemple - implémentation serveur

Constructeur (température initiale) et méthode de récupération de la température:

    public TemperatureSensorServer() 
      throws java.rmi.RemoteException {
        temp = 98.0; }

    public double getTemperature() 
      throws java.rmi.RemoteException {
        return temp; }

28  Exemple - implémentation serveur

Méthodes d'ajout et de retrait de clients:

public void addTemperatureListener
              (TemperatureListener listener)
  throws java.rmi.RemoteException {
  System.out.println("adding listener -"+listener);
  list.add(listener); }

public void removeTemperatureListener
              (TemperatureListener listener)
  throws java.rmi.RemoteException {
  System.out.println("removing listener -"+listener);
  list.remove(listener); }

29  Exemple - implémentation serveur

Thread responsable du changement aléatoire de la température:

public void run()
{ Random r = new Random();
  for (;;)
    { try {
        int duration = r.nextInt() % 10000 +2000;
        if (duration < 0) duration = duration*(-1);
        Thread.sleep(duration); }

30  Exemple - implémentation serveur

      catch(InterruptedException ie) {}
      int num = r.nextInt();
      if (num < 0)
        temp += .5;
      else
        temp -= .5;
      notifyListeners(); } }
(notifyListeners() est la méthode suivante, chargée de broadcaster le changement d'événements à tous les clients enregistrés)

31  Exemple - implémentation du serveur

private void notifyListeners() {
  for (Enumeration e = list.elements(); e.hasMoreElements();) 
  { TemperatureListener listener = 
        (TemperatureListener) e.nextElement();
    try {
      listener.temperatureChanged(temp);
    } catch(RemoteException re) {
        System.out.println("removing listener -"+listener);
        list.remove(listener); } } }
(on fait simplement appel, pour chaque client, à la méthode de notification temperatureChanged)

32  Exemple - implémentation du serveur

Enregistrement du service auprès du rmiregistry (éventuellement fourni à la ligne de commande):

public static void main(String args[]) {
  System.out.println("Loading temperature service");
  try {
    TemperatureSensorServer sensor = 
        new TemperatureSensorServer();
    String registry = "localhost";
    if (args.length >= 1)
      registry = args[0];
    String registration = "rmi://"+registry+
                          "/TemperatureSensor";
    Naming.rebind(registration,sensor);

33  Exemple

Démarrage du thread en charge de changer aléatoirement la température, et gestion des exceptions:

    Thread thread = new Thread(sensor);
    thread.start(); }
  catch (RemoteException re) {
    System.err.println("Remote Error - "+re); }
  catch (Exception e) {
    System.err.println("Error - "+e); } } }

34  Exemple - implémentation clients

import java.rmi.*;
import java.rmi.server.*;

public class TemperatureMonitor extends UnicastRemoteObject
    implements TemperatureListener {
    public TemperatureMonitor() throws RemoteException {}
(étend UnicastRemoteObject car serveur également! De même implémente TemperatureListener) Rq: constructeur vide (celui d'Object en fait).

35  Exemple - implémentation clients

Recherche du service serveur d'événements:

public static void main(String args[]) {
  System.out.println("Looking for temperature sensor");
  try {
    String registry = "localhost";
    if (args.length >= 1)
    registry = args[0];
    String registration = "rmi://"+registry+
                          "/TemperatureSensor";
    Remote remoteService = Naming.lookup(registration);
    TemperatureSensor sensor = (TemperatureSensor) 
                                 remoteService;

36  Exemple - implémentation clients

Création d'un moniteur et enregistrement auprès du serveur d'événements:

    double reading = sensor.getTemperature();
    System.out.println("Original temp : "+reading);
    TemperatureMonitor monitor = new TemperatureMonitor();
    sensor.addTemperatureListener(monitor);

37  Exemple - implémentation clients

Gestion des exceptions:

  } catch(NotBoundException nbe) {
      System.out.println("No sensors available"); }
    catch (RemoteException re) {
      System.out.println("RMI Error - "+re); }
    catch (Exception e) {
      System.out.println("Error - "+e); } }

38  Exemple - implémentation clients

Implémentation de la méthode de rappel:

public void temperatureChanged(double temperature)
  throws java.rmi.RemoteException {
  System.out.println("Temperature change event : "
                    +temperature);
}

39  Compilation

[goubaul1@cher Ex3]$ javac *.java
[goubaul1@cher Ex3]$ rmic TemperatureMonitor
[goubaul1@cher Ex3]$ rmic TemperatureSensorServer

40  Exécution

[goubaul1@cher Ex3]$ rmiregistry &
[goubaul1@cher Ex3]$ java TemperatureSensorServer
Loading temperature service
Premier client (sur loire):

[goubaul1@loire Ex3]$ rmiregistry &
[goubaul1@loire Ex3]$ java TemperatureMonitor cher
Looking for temperature sensor
Original temp : 100.0
Temperature change event : 99.5
Temperature change event : 100.0
Temperature change event : 100.5

41  Exécution

Temperature change event : 100.0
Temperature change event : 100.5
Temperature change event : 101.0
Temperature change event : 100.5
Temperature change event : 100.0
Temperature change event : 100.5
Temperature change event : 101.0
Temperature change event : 101.5

42  Exécution

On voit alors sur la console de cher:

adding listener -TemperatureMonitor_Stub[RemoteStub 
[ref: [endpoint:[129.104.254.64:3224](remote),
objID:[6e1408:f29e197d47:-8000, 0]]]]
Rajoutons un moniteur sur doubs:

[goubaul1@doubs Ex3]$ rmiregistry &
[goubaul1@doubs Ex3]$ java TemperatureMonitor cher
Looking for temperature sensor
Original temp : 101.5
Temperature change event : 102.0

43  Exécution

Temperature change event : 102.5
Temperature change event : 103.0
Temperature change event : 102.5
Temperature change event : 103.0
Temperature change event : 103.5
Temperature change event : 102.5
Temperature change event : 102.0

44  Exécution

Ce qui produit sur cher:

adding listener -TemperatureMonitor_Stub[RemoteStub 
[ref: [endpoint:[129.104.254.57:3648](remote),
objID:[6e1408:f29de7882e:-8000, 0]]]]
On voit bien que les températures et événements sont synchronisés avec l'autre client sur loire:

Temperature change event : 102.0

45  Exécution

Temperature change event : 102.5
Temperature change event : 103.0
Temperature change event : 102.5
Temperature change event : 103.0
Temperature change event : 103.5
^C
[goubaul1@loire Ex3]$ 

46  Exécution

On a interrompu sur loire, du coup sur cher:

removing listener -TemperatureMonitor_Stub[RemoteStub 
[ref: [endpoint:[129.104.254.64:3224](remote),
objID:[6e1408:f29e197d47:-8000, 0]]]]
On interrompt par Control-C sur doubs, du coup sur cher:

removing listener -TemperatureMonitor_Stub[RemoteStub 
[ref: [endpoint:[129.104.254.57:3648](remote),
objID:[6e1408:f29de7882e:-8000, 0]]]]

47  JavaParty

Il est intéressant de regarder l'implémentation du package.

48  JavaParty - exemple

package examples;

public remote class HelloJP {
  public void hello() {
    // Print on the console of the virtual machine where the
    // object lives
    System.out.println("Hello JavaParty!");
  }

49  Exemple (fin)

 
  public static void main(String[] args) {
    for (int n = 0; n < 10; n++) {
      // Create a remote object on some node
      HelloJP world = new HelloJP();
      // Remotely invoke a method
      world.hello();
    }
  }
}

50  Compilation et exécution

> mkdir classes
> jpc -d classes HelloJP.java
> jpinvite HelloJP
Hello...
Remarque: jpc -genjpsource permet de générer le code JAVA (RMI) correspondant. (-g permet d'avoir des messages d'erreur à l'exécution améliorés)

51  Utilisation de synchronized

Pas plus compliqué qu'en JAVA, même entre objets distants:

public remote class R {
  public synchronized void foo() {
    // A synchronized remote method
  }
}

52  Utilisation de synchronized

public void foo() {
  R remoteObject = ...;

  synchronized (remoteObject) {
    // A block synchronized 
    // on a remote object
  }
}

53  Localité des données

54  Localité des données

import jp.lang.DistributedRuntime;

public void foo() {
  int cnt = DistributedRuntime.getMachineCnt();
  for (int n = 0; n < cnt; n++) {
    /** @at n */
    R r = new R();
  }
}

55  Migration

boolean success = jp.lang.DistributedRuntime.migrate(obj, n);

56  Classes résidentes

remote class R implements jp.lang.Resident {
  ...
}
Intérêt: meilleures performances.


This document was translated from LATEX by HEVEA.