Cześć! Dzisiaj zajmiemy się dość ciekawym tematem: Java RMI. Oznacza to zdalne wywołanie metody. Możesz użyć RMI, aby zezwolić dwóm programom na wzajemną komunikację, nawet jeśli znajdują się na różnych komputerach. Czy to brzmi fajnie? :) I nie jest to takie trudne! W dzisiejszej lekcji przeanalizujemy elementy interakcji RMI i wymyślimy, jak ją skonfigurować. Pierwszą rzeczą, której potrzebujemy, jest klient i serwer. Naprawdę nie musimy zagłębiać się w terminologię komputerową. Jeśli chodzi o RMI, to są to tylko dwa programy. Jeden z nich będzie zawierał obiekt, a drugi będzie wywoływał metody na tym obiekcie. Wywoływanie metod obiektu, który istnieje w innym programie — to coś, czego jeszcze nie robiliśmy! Czas spróbować! :) Aby uniknąć ugrzęźnięcia, pozwól s, aby nasz program był prosty. Ogólnie rzecz biorąc, serwer wykonuje pewne obliczenia wymagane przez klienta. I tak będzie u nas. Nasz serwer będzie prostym programem kalkulatora. Będzie miał tylko jedną metodę:pomnóż() . Pomnoży dwie liczby przesłane do niego przez program kliencki, a następnie zwróci wynik. Przede wszystkim potrzebujemy interfejsu:

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

public interface Calculator extends Remote {

   int multiply(int x, int y) throws RemoteException;
}
Dlaczego potrzebujemy interfejsu? Ponieważ RMI opiera się na tworzeniu serwerów proxy, o czym uczyłeś się na poprzednich lekcjach . Jak zapewne pamiętasz, pracujemy z serwerami proxy poprzez interfejsy, a nie klasy. Nasz interfejs ma 2 ważne wymagania!
  1. Musi rozszerzyć interfejs Remote.
  2. Wszystkie jego metody muszą zgłaszać RemoteException (IDE nie zrobi tego automatycznie — musisz dodać to ręcznie!).
Teraz musimy stworzyć klasę serwera, która implementuje nasz interfejs kalkulatora . RMI w praktyce - 2Tutaj też wszystko jest dość proste:

import java.rmi.RemoteException;

public class RemoteCalculationServer implements Calculator {

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

}
Nie ma tu właściwie co komentować :) Teraz musimy napisać program serwera, który skonfiguruje i uruchomi nasz obiekt kalkulatora. będzie wyglądać tak:

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);

   }
}
Rozwiążmy to :) W pierwszym wierszu deklarujemy pewną zmienną typu String:

public static final String UNIQUE_BINDING_NAME = "server.calculator";
Ten ciąg jest unikalną nazwą zdalnego obiektu. Nasz program kliencki używa tej nazwy do znalezienia naszego serwera: zobaczysz to później. Następnie tworzymy nasz obiekt kalkulatora:

final RemoteCalculationServer server = new RemoteCalculationServer();
Tutaj wszystko jest jasne. Co dalej, jest ciekawsze:

final Registry registry = LocateRegistry.createRegistry(2732);
Ten obiekt rejestru jest rejestrem obiektów zdalnych. Są to obiekty, do których inne programy mają zdalny dostęp :) Przekazaliśmy numer 2732 do metody LocateRegistry.createRegistry() . To jest numer portu — unikalny numer, którego inne programy użyją do znalezienia naszego rejestru obiektów (znowu zobaczysz to poniżej). Idąc dalej... Zobaczmy, co dzieje się w następnym wierszu:

Remote stub = UnicastRemoteObject.exportObject(server, 0);
Tworzymy kod pośredniczący w tej linii. Kod pośredniczący obejmuje całe zdalne połączenie. Można to uznać za najważniejszy element RMI. Co to robi?
  1. Otrzymuje wszystkie informacje o zdalnym wywołaniu jakiejś metody.
  2. Jeśli metoda ma parametry, kod pośredniczący dokona ich deserializacji. Zwróć uwagę na ten punkt! Argumenty, które przekazujesz do zdalnie wywoływanych metod, muszą być możliwe do serializacji (w końcu zostaną przesłane przez sieć). Nie stanowi to dla nas problemu — po prostu przesyłamy liczby. Ale jeśli przesyłasz obiekty, nie zapomnij o tym wymogu!
  3. Następnie kod pośredniczący wywołuje żądaną metodę.
Przekazujemy nasz obiekt serwera kalkulatora do metody UnicastRemoteObject.exportObject() . W ten sposób umożliwiamy zdalne wywoływanie jego metod. Pozostała tylko jedna rzecz do zrobienia:

registry.bind(UNIQUE_BINDING_NAME, stub);
„Rejestrujemy” nasz kod pośredniczący w zdalnym rejestrze obiektów pod nazwą, którą wymyśliliśmy na samym początku. Teraz klient będzie mógł go znaleźć! Być może zauważyłeś, że główny wątek programu usypiamy na końcu:

Thread.sleep(Integer.MAX_VALUE);
Potrzebujemy tylko, aby serwer działał przez długi czas. W IDE uruchomimy jednocześnie dwie metody main() : po pierwsze metodę main() serwera (w klasie ServerMain , którą już napisaliśmy), a po drugie metodę main() klienta (w klasie ClientMain , o czym napiszemy poniżej). Ważne jest, aby program serwera nie został zakończony podczas uruchamiania klienta, więc po prostu usypiamy go na długi czas. W każdym razie będzie działać :) Teraz możemy uruchomić metodę main() naszego serwera. Pozwól mu działać i poczekaj, aż program kliencki wywoła jakąś metodę :) Teraz napiszmy program kliencki! Wyśle liczby na nasz serwer w celu pomnożenia.

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);
   }
}
Wygląda na proste. Ale co tu się dzieje? Po pierwsze, klient musi znać unikalną nazwę obiektu, którego metody będzie wywoływał zdalnie. W związku z tym w programie klienckim utworzyliśmy publiczny static final String UNIQUE_BINDING_NAME = "server.calculator"; zmienny. Następnie w metodzie main() uzyskujemy dostęp do rejestru zdalnych obiektów. W tym celu musimy wywołać metodę LocateRegistry.getRegistry() i przekazać w programie ServerMain numer portu, który posłużył do utworzenia naszego rejestru (port 2732; ten numer jest przykładowy — można spróbować użyć innego numeru):

final Registry registry = LocateRegistry.getRegistry(2732);
Teraz musimy tylko pobrać żądany obiekt z rejestru! To proste, bo znamy jego unikalną nazwę!

Calculator calculator = (Calculator) registry.lookup(UNIQUE_BINDING_NAME);
Zwróć uwagę na rzutowanie typu. Otrzymany obiekt rzutujemy na interfejs Kalkulatora , a nie na klasę RemoteCalculationServer . Jak powiedzieliśmy na początku lekcji, RMI opiera się na proxy, więc zdalne wywołania są dostępne tylko dla metod interfejsu, a nie metod klas. Na koniec zdalnie wywołujemy metodę multiple() na naszym obiekcie i wysyłamy wynik do konsoli.

int multiplyResult = calculator.multiply(20, 30);
System.out.println(multiplyResult);
Metoda main() klasy ServerMain działa już od dłuższego czasu. Teraz nadszedł czas, aby uruchomić metodę main() w programie klienckim ( ClientMain )! Wyjście konsoli:

600
Otóż ​​to! Nasz program (a właściwie dwa programy!) zrobił to, co miał zrobić :) Jeśli masz czas i ochotę, możesz to trochę urozmaicić. Na przykład spraw, aby kalkulator obsługiwał cztery standardowe operacje arytmetyczne i przekazywał liczby nie jako typy pierwotne, ale jako obiekty CalculationInstance(int x, int y) . Do zobaczenia na następnej lekcji! :)