John Squirrels
Szint
San Francisco

Java RMI

Megjelent a csoportban
Szia! Ma egy meglehetősen érdekes témával foglalkozunk: a Java RMI-vel. Ez a Remote Method Invocation rövidítése. Az RMI segítségével két program kommunikálhat egymással, még akkor is, ha különböző számítógépeken vannak. Ez jól hangzik? :) És nem is olyan nehéz megcsinálni! A mai leckében elemezzük az RMI interakció elemeit, és kitaláljuk, hogyan konfigurálhatjuk. Az első dolog, amire szükségünk van, az egy kliens és egy szerver. Nem igazán kell mélyen belemerülnünk a számítógépes terminológiába. Ami az RMI-t illeti, ez csak két program. Az egyik tartalmaz egy objektumot, a másik pedig metódusokat hív meg az objektumon. Egy másik programban létező objektum metódusainak meghívása – ezt most még nem tettük meg! Ideje kipróbálni! :) Hogy ne akadjunk fenn, hagyjuk s legyen programunk egyszerű. Általánosságban elmondható, hogy a szerver végrehajt néhány számítást, amelyet az ügyfél kér. És ez nálunk is így lesz. Szerverünk egy egyszerű számolóprogram lesz. Csak egy módszere lesz:szorzás() . Megszorozza a kliensprogram által neki küldött két számot, majd visszaadja az eredményt. Először is szükségünk van egy interfészre:

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Calculator extends Remote {

   int multiply(int x, int y) throws RemoteException;
}
Miért van szükségünk interfészre? Mivel az RMI proxy-k létrehozására támaszkodik, amelyeket a korábbi leckéken tanult meg . Amint valószínűleg emlékszel, a proxykkal interfészeken keresztül dolgozunk, nem osztályokon keresztül. A felületünkkel szemben 2 fontos követelmény van!
  1. Ki kell bővítenie a távoli felületet.
  2. Minden metódusának távoli kivételt kell adnia (az IDE ezt nem teszi meg automatikusan – ezt manuálisan kell hozzáadnia!).
Most létre kell hoznunk egy szerverosztályt, amely megvalósítja a Számológép felületünket. RMI a gyakorlatban - 2Itt is minden nagyon egyszerű:

import java.rmi.RemoteException;

public class RemoteCalculationServer implements Calculator {

   @Override
   public int multiply(int x, int y) throws RemoteException {
       return x*y;
   }

}
Itt nem igazán van mit kommentálni :) Most egy szerver programot kell írnunk, ami beállítja és futtatja a számológép objektumunkat. Így fog kinézni:

import java.rmi.AlreadyBoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

public class ServerMain {

   public static final String UNIQUE_BINDING_NAME = "server.calculator";

   public static void main(String[] args) throws RemoteException, AlreadyBoundException, InterruptedException {

       final RemoteCalculationServer server = new RemoteCalculationServer();

       final Registry registry = LocateRegistry.createRegistry(2732);

       Remote stub = UnicastRemoteObject.exportObject(server, 0);
       registry.bind(UNIQUE_BINDING_NAME, stub);

       Thread.sleep(Integer.MAX_VALUE);

   }
}
Találjuk ki :) Az első sorban deklarálunk néhány String változót:

public static final String UNIQUE_BINDING_NAME = "server.calculator";
Ez a karakterlánc a távoli objektum egyedi neve. Kliensprogramunk ezt a nevet használja szerverünk megtalálásához: ezt később látni fogja. Ezután létrehozzuk a számológép objektumunkat:

final RemoteCalculationServer server = new RemoteCalculationServer();
Itt minden világos. Ami ezután jön, az még érdekesebb:

final Registry registry = LocateRegistry.createRegistry(2732);
Ez a regisztrációs objektum a távoli objektumok nyilvántartása. Ezek olyan objektumok, amelyeket más programok távolról is elérhetnek :) A 2732-es számot átadtuk a LocateRegistry.createRegistry() metódusnak. Ez a portszám – egy egyedi szám, amelyet más programok fognak használni az objektumnyilvántartásunk megtalálásához (ezt alább ismét látni fogja). Tovább haladva... Lássuk, mi történik a következő sorban:

Remote stub = UnicastRemoteObject.exportObject(server, 0);
Ebben a sorban egy csonkot hozunk létre. Egy csonk a teljes távoli hívást magába foglalja. Ezt tekintheti az RMI legfontosabb elemének. Mit csinal?
  1. Minden információt megkap valamilyen módszer távoli hívásáról.
  2. Ha a metódusnak vannak paraméterei, a csonk deszerializálja azokat. Erre a pontra figyelj! A távolról hívott metódusoknak átadott argumentumoknak szerializálhatóaknak kell lenniük (végül is a hálózaton keresztül kerülnek továbbításra). Ez nem jelent problémát számunkra – csak számokat továbbítunk. De ha tárgyakat továbbít, ne felejtse el ezt a követelményt!
  3. Ezt követően a csonk meghívja a kívánt metódust.
A számológép-kiszolgáló objektumunkat a UnicastRemoteObject.exportObject() metódusnak adjuk át. Ezzel lehetővé tesszük metódusainak távoli meghívását. Már csak egy dolog van hátra:

registry.bind(UNIQUE_BINDING_NAME, stub);
A távoli objektum-nyilvántartásba "regisztráljuk" a csonkunkat azon a néven, amelyet a legelején kitaláltunk. Most az ügyfél megtalálhatja! Talán észrevetted, hogy a program főszálát elaltattuk a végén:

Thread.sleep(Integer.MAX_VALUE);
Csak arra van szükségünk, hogy a szerver sokáig működjön. Az IDE-ben egyszerre két main() metódust fogunk elindítani: először a szerver main() metódusát (a ServerMain osztályban, amit már írtunk), másodszor pedig a kliens main() metódusát (a ClientMain osztályban, amelyet alább írunk). Fontos, hogy a szerver program ne álljon le a kliens indításakor, így csak hosszú időre alvó állapotba helyezzük. Mindenesetre futni fog tovább :) Most már futtathatjuk szerverünk main() metódusát. Hagyjuk futni, és várjuk meg, hogy a kliens program meghívjon valamilyen metódust :) Most írjuk meg a kliens programot! Számokat küld a szerverünkre szorzás céljából.

import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class ClientMain {

   public static final String UNIQUE_BINDING_NAME = "server.calculator";

   public static void main(String[] args) throws RemoteException, NotBoundException {

       final Registry registry = LocateRegistry.getRegistry(2732);

       Calculator calculator = (Calculator) registry.lookup(UNIQUE_BINDING_NAME);

       int multiplyResult = calculator.multiply(20, 30);

       System.out.println(multiplyResult);
   }
}
Egyszerűnek tűnik. De mi folyik itt? Először is, az ügyfélnek ismernie kell annak az objektumnak az egyedi nevét, amelynek metódusait távolról hívni fogja. Ennek megfelelően az ügyfélprogramban létrehoztuk a nyilvános statikus végleges String UNIQUE_BINDING_NAME = "server.calculator"; változó. Ezután a main() metódusban hozzáférünk a távoli objektumok regiszteréhez. Ehhez meg kell hívnunk a LocateRegistry.getRegistry() metódust, és át kell adnunk a rendszerleíró adatbázisunk létrehozásához használt portszámot a ServerMain programban (2732-es port; ez a szám csak egy példa – megpróbálhat másik számot használni):

final Registry registry = LocateRegistry.getRegistry(2732);
Most már csak be kell szereznünk a kívánt objektumot a registry-ből! Ez könnyű, mert ismerjük egyedi nevét!

Calculator calculator = (Calculator) registry.lookup(UNIQUE_BINDING_NAME);
Ügyeljen a típusöntésre. A kapott objektumot a Calculator felületére öntjük , nem a RemoteCalculationServer osztályba. Ahogy a lecke elején mondtuk, az RMI proxyra támaszkodik, így a távoli hívások csak egy interfész metódusaihoz érhetők el, egy osztály metódusaihoz nem. Végül távolról meghívjuk a multiply() metódust az objektumunkon, és az eredményt kiadjuk a konzolra.

int multiplyResult = calculator.multiply(20, 30);
System.out.println(multiplyResult);
A ServerMain osztály main() metódusa már régóta fut. Itt az ideje a main() metódus futtatásának a kliens programban ( ClientMain )! Konzol kimenet:

600
Ez az! A programunk (igazából két program!) azt csinálta, amit kellett :) Ha van időd és kedved, ezt egy kicsit megfűszerezheted. Például állítsa be, hogy a számológép támogassa a négy szabványos aritmetikai műveletet, és a számokat ne primitív típusként adja át, hanem CalculationInstance(int x, int y) objektumként. Találkozunk a következő leckében! :)
Hozzászólások
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION