Подробно про RMI в Java
Удаленный вызов метода – это способ, которым программист использует язык программирования Java и его среду разработки удаленно.
Что такое RMI в Java?
RMI (Remote Method Invocation) – это API, который предоставляет механизм для создания распределенного приложения на Java. RMI позволяет объекту вызывать методы объекта, запущенного в другой JVM. Удаленный вызов метода обеспечивает удаленную связь между приложениями с использованием двух объектов.
Объект-заглушка на клиентском компьютере создает информационный блок и отправляет эту информацию на сервер. Блок состоит из:
- Идентификатор удаленного объекта, который будет использоваться;
- Имя метода, который должен быть вызван;
- Параметры для удаленной JVM.

Скелетный объект передает запрос от объекта-заглушки к удаленному объекту. Он выполняет следующие задачи:
- Вызывает нужный метод для реального объекта, присутствующего на сервере.
- Он передает параметры, полученные от объекта-заглушки, в метод.
Теперь рассмотрим шаги для создания приложения RMI.
Шаг 1: Определение удаленного интерфейса
Первое, что нам нужно сделать, это создать интерфейс. Это предоставит описание методов, которые могут быть вызваны удаленными клиентами. Этот интерфейс должен расширять интерфейс Remote, а прототип метода в интерфейсе должен выдавать исключение RemoteException.
Шаг 2: Реализация удаленного интерфейса
Следующим шагом является реализация удаленного интерфейса. Для реализации удаленного интерфейса класс должен быть расширен до класса UnicastRemoteObject пакета java.rmi. Кроме того, конструктор по умолчанию должен быть создан для выброса java.rmi.RemoteException от его родительского конструктора.
Шаг 3: Создание объектов Stub и Skeleton из класса реализации с использованием rmic
Инструмент RMIC используется для вызова компилятора RMI, который создает объекты Stub и Skeleton. Его прототип – имя класса RMIC.
ШАГ 4: Запустите RMIregistry
Чтобы запустить службу реестра, введите в командной строке команду запуска RMIregistry.
ШАГ 5. Создание и выполнение прикладной программы сервера
Следующим шагом является создание прикладной программы сервера и ее выполнение в отдельной командной строке.
- Программа сервера использует метод createRegistry класса LocateRegistry для создания rmiregistry в JVM сервера с номером порта, передаваемым в качестве аргумента.
- Метод rebind класса Naming используется для привязки удаленного объекта к новому имени.
Шаг 6. Создание и выполнение программы клиентского приложения
Последний шаг – создание программы клиентского приложения и ее выполнение в отдельной командной строке. Метод lookup класса Naming используется для получения ссылки на объект Stub
Вышеуказанная клиентская и серверная программа выполняется на одной машине, поэтому используется localhost. Чтобы получить доступ к удаленному объекту с другого компьютера, локальный хост должен быть заменен IP-адресом, на котором находится удаленный объект.
3. RMI — Удаленный вызов методов на Java
Java RMI (Remote Method Invocation – удаленный вызов методов) представляет собой тип удаленного вызова процедур, независимый от сети, облегченный и полностью переносимый, так как написан на языке Java.
Технология RMI основана на более ранней подобной технологии RPC (Remote Procedure Call – удаленного вызова процедур) для процедурного программирования, разработанной в 80-х годах. RPC позволяет процедуре вызывать функцию на другом компьютере столь же легко, как если бы эта функция была частью программы, выполняющейся на том же компьютере. Для устранения недостатков RPC была разработана технология RMI, которая обслуживает маршалинг данных через сеть и дает возможность программам на Java передавать законченные объекты Java с помощью механизма сериализации объектов Java.
Сериализация — это процесс сохранения состояния объекта в последовательность байт; десериализация это процесс восстановления объекта, из этих байт. Java Serialization API предоставляет стандартный механизм для создания сериализуемых объектов.
Маршалинг — по смыслу похож на сериализацию, процесс преобразования представления объекта в памяти в формат данных, пригодный для хранения или передачи. Применительно же к компьютерным сетям, маршалинг означает процесс упаковки данных и преобразования их в стандартный вид перед передачей по сети так, чтобы данные могли пройти через сетевые ограничители. Чтобы передать объект во внешнюю сеть, он должен быть преобразован в поток данных, соответствующий структуре пакетов сетевого протокола. Части данных содержатся в буфере до того момента, пока не будут упакованы. Когда данные переданы, компьютер-получатель преобразует упакованные данные обратно в объект.
1 Основные шаги работы с RMI
Для реализации приложения, основанного на RMI, необходимо выполнить определённую последовательность действий:
- Определить удаленный интерфейс, согласованный с сервером.
- Написать код сервера.
- Запустить программу rmic (Java RMI stub compiler – компилятор заглушек RMI) для генерации связующего кода.
- Написать код клиента.
- Убедиться, что на сервере запущен RMI реестр (программа rmiregistry).
- Запустить сервер.
- Запустить одного или нескольких клиентов.
2 Удалённый интерфейс
Процедуры RMI определяют с помощью известного механизма Java – интерфейсов.
Интерфейс имеет следующий общий синтаксис [5]:
Примечание: Интерфейс не предоставляет никакой реализации, поэтому является абстрактным по определению. Это означает, что нельзя создать его экземпляр, но классы могут его реализовывать, предоставляя реализацию для его прототипов методов.
Удаленные интерфейсы должны быть подклассами java.rmi.Remote , при этом и клиент и сервер должны находиться в одном пакете Java. Все параметры удаленных методов должны относиться или к примитивным типам ( int, double и т.п.), либо реализовывать интерфейс java.io.Serializable. Каждый метод удаленного интерфейса должен декларировать java.rmi.RemoteException в своем разделе throws в дополнение ко всем остальным специфичным для приложения исключениям.
Пример удалённого интерфейса, который предоставляет службу точного времени:
3 Реализация удалённого интерфейса (серверная часть)
Сервер должен содержать класс, который наследуется от UnicastRemoteObject (extends UnicastRemoteObject) и реализует удаленный интерфейс (implements имя интерфейса). Этот класс может также иметь дополнительные методы, но для клиента будут доступны только методы удаленного интерфейса, так как клиент будет получать только ссылку на интерфейс, а не на класс, реализующий его.
Пример реализации удаленного интерфейса PerfectTimeI:
Класс UnicastRemoteObject (пакет java.rmi.server ) предоставляет удалённым объектам базовые функциональные возможности для обслуживания удалённых запросов. Конструкторы и методы класса UnicastRemoteObject возбуждают контролируемое исключение RemoteException, поэтому его подклассы должны определять конструкторы, также возбуждающие исключение RemoteException. По этой причине в подклассе необходимо явно определить конструктор для удаленного объекта, даже если определён только конструктор по умолчанию, который вызывает конструктор базового класса.
Конструктор класса UnicastRemoteObject экспортирует объект, чтобы сделать его доступным для приёма удалённых вызовов. Экспорт объекта даёт возможность удалённому объекту ожидать соединений с клиентами через анонимный порт. Это даёт возможность объекту осуществлять однонаправленное взаимодействие (посредством вызова методов) с использованием сокетов.
4 Настройка реестра
Реестр RMI обслуживается утилитой rmiregistry, и должен быть запущен как отдельный процесс на компьютере. Однако, если наше приложение является единственным, использующим реестр, его можно запускать в самой программе:
Для экспорта удалённого объекта необходимо определить URL , который клиент сможет использовать для получения удалённой ссылки на объект. URL обычно имеет следующий вид:
"rmi://хост:порт/имя удалённого объекта"
где хост – имя компьютера, который выполняет сервер реестра и на нём же выполняется удалённый объект, порт реестра RMI по умолчанию 1099, имя удалённого объекта будет использовать клиент для поиска удалённого объекта в реестре.
Если реестр выполняется на локальном компьютере, то можно использовать следующий URL:
Для связывания удалённого объекта с реестром используются статические методы Naming.bind() или Naming.rebind() класса Naming(пакета java.rmi ). Разница между этими методами заключается в том, что если имя удалённого объекта уже есть в реестре, то bind() вызовет исключение AlreadyBoundException, а rebind() заменит существующую запись.
5 Заглушки и скелеты
Для обеспечения правильной работы RMI приложения необходимо создать заглушки и скелеты (вместо скелета используются также термины скелетон или каркас), обеспечивающие сетевое соединение. Для этого служит утилита rmic, входящая в состав Java 2 SE. После её запуска в папке с проектом должны быть созданы два класса _Stub.class и _Skel.class. В нашем примере это будут:
6 Использование удалённого объекта (клиентская часть)
Клиент, как уже упоминалось выше, взаимодействует с публичным интерфейсом, а не с его реализацией, скрытой от конечного пользователя. Приведённый ниже пример демонстрирует вызов метода getPerfectTime() удалённого объекта PerfectTime, для получения системного времени последнего через интерфейс PerfectTimeI.
Для поиска в реестре удалённого объекта PerfectTime используется статический метод Naming.lookup() (пакета java.rmi ).
7 Запуск приложения
Для работы приложения необходимо проделать последовательность действий, описанную в разделе 1. После того, как написан код приложений, произведена настройка реестра, созданы заглушки и скелеты запускается сначала серверная часть, а затем клиентская – работа сделана.
Все о реестре Java RMI и как его использовать

RMI обозначает удаленный вызов метода и, как видно из названия, является протоколом для Java-программы для вызова метода объекта, запущенного на другом компьютере. Он предоставляет API (Интерфейс прикладного программирования) для экспорта объекта из одной программы (называемой сервером) и вызова методов этого объекта из другой программы (называемой клиентом), возможно, работающих на другом компьютере.
Реестр Java RMI является ключевым компонентом системы Java RMI и предоставляет централизованный каталог для серверов для регистрации служб и для клиентов для поиска этих служб. В этой статье мы узнаем, как реализовать сервер для предоставления объекта и клиента для вызова метода на сервере, а также для регистрации и поиска службы в реестре RMI.

Объявление интерфейса сервера
Чтобы узнать тонкости работы системы Java RMI, давайте реализуем простой объект сервера, предоставляющий метод для принятия имени и возврата приветствия. Вот определение интерфейса объекта:
Название интерфейса называется Приветствие . Он предоставляет единственный метод, называемый greet (), который принимает имя и возвращает подходящее приветствие.
Чтобы пометить этот интерфейс как экспортируемый, он должен расширить интерфейс java.rmi.Remote . Кроме того, метод должен объявить предложение throws , перечисляющее java.rmi.RemoteException в дополнение к любым исключениям для конкретного приложения. Исключения Это делается для того, чтобы клиентский код мог обрабатывать (или распространять) ошибки вызова удаленных методов, такие как хост-не найден , сбой соединения и т. Д.
Реализация объекта сервера
После объявления интерфейса (который используется клиентами) мы реализуем объект на стороне сервера и предоставляем метод greet (), как показано. Для форматирования приветствия используется простая строка формата.
Основной метод сервера
Давайте теперь соберем все эти части вместе и реализуем метод main () сервера. Давайте пройдемся по каждому из соответствующих шагов.
- Первым шагом является создание реализации объекта сервера.
- Затем мы получаем заглушку для объекта сервера из среды выполнения RMI. Заглушка реализует тот же интерфейс, что и объект сервера. Однако метод реализует необходимую связь с объектом удаленного сервера. Эта заглушка используется клиентом для прозрачного вызова метода на объекте сервера.
- Как только заглушка получена, мы передаем эту заглушку в реестр RMI для привязки к указанной именованной службе. Когда клиент запрашивает реализацию этой службы, он получает заглушку, которая знает, как связаться с объектом сервера. В дальнейшем статический метод LocateRegistry.getRegistry () используется для получения ссылки на локальный реестр. Затем метод rebind () используется для привязки имени к заглушке.
Полный основной метод.
Сборка сервера
Давайте теперь посмотрим на создание сервера. Для простоты мы строим с помощью командной строки в Linux, а не с помощью инструмента сборки, такого как Maven.
Следующее компилирует исходные файлы в файлы классов в целевом каталоге.
Соберите файлы классов в файл JAR для выполнения.
Мы также собираем файлы интерфейса, необходимые для компиляции клиента в библиотеку JAR.
Реализация клиента
Давайте теперь посмотрим на реализацию клиента, используемого для вызова методов объекта сервера.
- Как и на сервере, получите ссылку на реестр, указав имя хоста, на котором работает реестр, и номер порта.
- Далее ищем сервис в реестре. Метод lookup () возвращает заглушку, которую можно использовать для вызова служб.
- И вызвать метод, передавая необходимые аргументы. Здесь мы получаем приветствие, передавая имя и распечатывая его.
Полный код клиента:
Реестр RMI
Теперь давайте запустим серверную программу, чтобы она могла начать обслуживать запросы.
Причина, по которой вы получаете это исключение, заключается в следующем: обратите внимание, что из кода сервера он пытается подключиться к локальному реестру через порт 1099. В случае сбоя вы получите это исключение.
Решение — запустить Реестр RMI. Реестр RMI — это программа, поставляемая с виртуальной машиной Java, и называется rmiregistry . Он должен быть расположен в каталоге bin установки Java Virtual Machine. Запуск это так же просто, как:
По умолчанию реестр прослушивает порт 1099. Чтобы прослушивать другой порт, укажите номер порта следующим образом:
Проверьте, действительно ли имеется прослушиватель на указанном порту с помощью команды netstat. :
Запуск сервера
Давайте теперь попробуем снова запустить сервер.
Снова исключение! Что на этот раз?
Сервер не может загрузить интерфейсный класс server.Greeting . Это происходит потому, что реестр RMI не может загрузить требуемый класс. Таким образом, вам необходимо указать расположение необходимых классов. Один из способов сделать это — указать переменную среды CLASSPATH:
Попытка запустить сервер снова дает:
Теперь сервер работает.
Запуск клиента
После того, как все части собраны и выполнены, запустить клиент просто. Для выполнения необходимы соответствующие JAR-файлы. К ним относятся класс, содержащий метод main () , и класс интерфейса. Он принимает аргументы, указывающие, где работает реестр RMI, и имя для приветствия.
Резюме
Java RMI предоставляет API и инструменты для облегчения удаленного выполнения кода. Вы можете реализовать сервер, который регистрирует объект службы в реестре Java RMI. Клиенты могут запросить реестр и получить заглушку объекта службы для вызова методов службы. Как показывает этот пример, все довольно просто.
Вы используете Java RMI в своем проекте? Каким был ваш опыт? Есть ли альтернативы, которые вы исследовали? Пожалуйста, дайте нам знать в комментариях ниже.