JavaRush /Java блог /Random UA /RMI: практика використання
Professor Hans Noodles
41 рівень

RMI: практика використання

Стаття з групи Random UA
Вітання! Сьогодні ми розглянемо досить цікаву тему – RMI . Це розшифровується як Remote Method Invocation – віддалений виклик методів. RMI: практика використання - 1За допомогою RMI ти можеш навчити дві програми спілкуватися між собою навіть якщо вони знаходяться на різних комп'ютерах. Звучить круто? :) А це не так вже й складно зробити! У сьогоднішній лекції ми розберемося, із яких частин складається RMI-взаємодія та як його налаштувати. Перше, що нам знадобиться – це клієнт та сервер. Можеш особливо не заглиблюватись у комп'ютерну термінологію. У випадку RMI це просто дві програми. Одна з них міститиме якийсь об'єкт, а друга викликатиме методи цього об'єкта. Викликати в одній програмі методи об'єкта, що знаходиться в іншій програмі, – такого ми ще не робабо! Саме час спробувати! :) Щоб не потонути в нетрях, нехай наша програма буде простою. Взагалі, на серверах зазвичай відбуваються якісь обчислення, які запитує клієнт. Так буде й у нас. У ролі сервера у нас буде проста програма-калькулятор. У неї буде лише один метод — multiply(). Він множитиме два числа, які йому відправила програма-клієнт, і повертатиме результат. Насамперед нам знадобиться інтерфейс:
import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Calculator extends Remote {

   int multiply(int x, int y) throws RemoteException;
}
Для чого нам інтерфейс? Справа в тому, що робота RMI заснована на створенні проксі, які ти вивчав в одній із минулих лекцій . А робота з проксі, як ти, мабуть, пам'ятаєш, ведеться саме на рівні інтерфейсів, а не класів. До нашого інтерфейсу є дві важливі вимоги!
  1. Він повинен успадковувати інтерфейс-маркер Remote.
  2. Всі його методи повинні викидати RemoteException (це не робиться в IDE автоматично, треба написати руками!).
Тепер нам треба створити клас-сервер, який реалізовуватиме наш інтерфейс Calculator. RMI: практика використання - 2Тут також усе досить просто:
import java.rmi.RemoteException;

public class RemoteCalculationServer implements Calculator {

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

}
Тут навіть коментувати особливо нічого :) Тепер нам потрібно написати програму-сервер, яка налаштовуватиме і запускатиме наш серверний клас-калькулятор. Вона виглядатиме ось так:
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);

   }
}
Давай розбиратися :) У першому рядку ми створюємо якусь рядкову змінну:
public static final String UNIQUE_BINDING_NAME = "server.calculator";
Цей рядок є унікальним ім'ям віддаленого об'єкта . За цим ім'ям програма-клієнт зможе знайти наш сервер: ти побачиш це пізніше. Далі ми створюємо наш об'єкт-калькулятор:
final RemoteCalculationServer server = new RemoteCalculationServer();
Тут усе зрозуміло. Далі вже цікавіше:
final Registry registry = LocateRegistry.createRegistry(2732);
Ця штука під назвою Registry - реєстр віддалених об'єктів . «Видалених» не в тому сенсі, що ми їх видалабо з комп'ютера, а в тому, що до об'єктів з цього регістру можливий віддалений доступ з інших програм :) У спосіб ми LocateRegistry.createRegistry()передали число 2732. Це номер порту. Якщо не знаєш, що таке порт — можна почитати ось тут , але зараз тобі достатньо запам'ятати, що це унікальний номер, за яким інші програми зможуть знайти наш реєстр об'єктів (це теж побачиш нижче). Їдемо далі. Подивимося, що у нас відбувається у наступному рядку:
Remote stub = UnicastRemoteObject.exportObject(server, 0);
У цьому рядку ми створюємо заглушку . Заглушка (stub) інкапсулює в собі весь процес віддаленого виклику. Можна сміливо сказати, що це найважливіший елемент RMI. Що вона робить?
  1. Приймає всю інформацію про віддалений виклик якогось методу.
  2. Якщо метод має параметри, заглушка десеріалізує їх. Зверніть увагу на цей пункт! Параметри, які ти передаєш методам для віддаленого дзвінка, повинні бути серіалізованими (адже вони передаватимуться по мережі). У нас такої проблеми немає – ми передаємо просто цифри. Але якщо ти будеш передавати об'єкти, не забудь про це!
  3. Після цього вона викликає необхідний спосіб.
Ми передаємо метод UnicastRemoteObject.exportObject()наш об'єкт-калькулятор server. Таким чином ми уможливлюємо віддалений виклик його методів. Нам залишилося зробити лише одне:
registry.bind(UNIQUE_BINDING_NAME, stub);
Ми реєструємо нашу заглушку в реєстрі віддалених об'єктів під тим ім'ям, яке придумали на самому початку. Тепер клієнт зможе її знайти! Можливо, ти звернув увагу, що наприкінці ми приспали головний потік програми:
Thread.sleep(Integer.MAX_VALUE);
Нам просто потрібно, щоб сервер працював довгий час. Адже ми будемо запускати в IDEa відразу два методи main(): спочатку серверний (у класі ServerMain, який ми вже написали), а потім - клієнтський (у класі ClientMain, який ми напишемо нижче). Важливо, щоб програма-сервер не вирубалася, поки ми запускатимемо клієнт, тому ми її просто приспали на довгий час. Працювати вона все одно :) Тепер ми можемо запустити метод main()нашого сервера. Нехай він працює і чекає, коли програма-клієнт викличе якийсь метод:) Тепер давай напишемо програму-клієнт! Вона надсилатиме нашому серверу числа для множення.
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);
   }
}
Вона має нескладний вигляд. Що тут відбувається? По-перше, клієнт має бути в курсі унікального імені об'єкта, методи якого він викликатиме віддалено. Тому в програмі-клієнті ми теж створабо змінну public static final String UNIQUE_BINDING_NAME = "server.calculator"; Далі, у методі main()ми отримуємо доступ до регістру віддалених об'єктів. Для цього нам потрібно викликати метод LocateRegistry.getRegistry()і передати туди номер порту, на якому створювали наш регістр у програмі ServerMain - порт 2732 (цей номер був обраний для прикладу, ти можеш спробувати використати інший):
final Registry registry = LocateRegistry.getRegistry(2732);
Тепер нам залишилося лише отримати з регістру потрібний об'єкт! Це легко, адже ми знаємо його унікальне ім'я!
Calculator calculator = (Calculator) registry.lookup(UNIQUE_BINDING_NAME);
Зверніть увагу на приведення типів. Ми наводимо отриманий об'єкт до інтерфейсу Calculator, а чи не до конкретного класу RemoteCalculationServer . Як ми говорабо на початку лекції, робота RMI заснована на використанні проксі, тому віддалений виклик доступний лише для методів інтерфейсів, а не класів. Наприкінці ми віддалено викликаємо метод multiply()у нашого об'єкта та виводимо результат у консоль.
int multiplyResult = calculator.multiply(20, 30);
System.out.println(multiplyResult);
Метод main()у класі ServerMainми вже давно запустабо, саме час запустити метод main()у програмі-клієнті ClientMain! Висновок у консоль: 600 Ось і все! Наша програма (навіть дві!) успішно виконала свою функцію:) Якщо у тебе є час і бажання, можеш трохи урізноманітнити її. Наприклад, зробити так, щоб калькулятор підтримував всі чотири стандартні операції, а як параметри передавалися не числа, а об'єкт CalculationInstance(int x, int y). Як додатковий матеріал можеш подивитися статті та приклади — ось тут: Побачимося на наступних заняттях! :)
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ