«1. Обзор

Когда двум JVM необходимо взаимодействовать, Java RMI является одним из вариантов, который нам нужен для этого. В этой статье мы запустим простой пример, демонстрирующий технологию Java RMI.

2. Создание сервера

Для создания сервера RMI необходимо выполнить два шага:

  1. Create an interface defining the client/server contract.
  2. Create an implementation of that interface.

2.1. Определение контракта

Прежде всего, давайте создадим интерфейс для удаленного объекта. Этот интерфейс расширяет интерфейс маркера java.rmi.Remote.

Кроме того, каждый метод, объявленный в интерфейсе, генерирует исключение java.rmi.RemoteException:

public interface MessengerService extends Remote {
    String sendMessage(String clientMessage) throws RemoteException;
}

Обратите внимание, однако, что RMI поддерживает полную спецификацию Java для сигнатур методов, если типы Java реализуют java. io.Сериализуемый.

В следующих разделах мы увидим, как клиент и сервер будут использовать этот интерфейс.

Для сервера мы создадим реализацию, часто называемую удаленным объектом.

Для клиента библиотека RMI динамически создает реализацию, называемую Stub.

2.2. Реализация

Кроме того, давайте реализуем удаленный интерфейс, снова называемый Remote Object:

public class MessengerServiceImpl implements MessengerService { 
 
    @Override 
    public String sendMessage(String clientMessage) { 
        return "Client Message".equals(clientMessage) ? "Server Message" : null;
    }

    public String unexposedMethod() { /* code */ }
}

Обратите внимание, что мы исключили предложение throws RemoteException из определения метода.

Для нашего удаленного объекта было бы необычно генерировать RemoteException, поскольку это исключение обычно зарезервировано для библиотеки RMI, чтобы вызвать ошибки связи с клиентом.

Отказ от этого также имеет то преимущество, что наша реализация не зависит от RMI.

Также любые дополнительные методы, определенные в удаленном объекте, но не в интерфейсе, остаются невидимыми для клиента.

3. Регистрация службы

После создания удаленной реализации нам необходимо привязать удаленный объект к реестру RMI.

3.1. Создание заглушки

Во-первых, нам нужно создать заглушку нашего удаленного объекта:

MessengerService server = new MessengerServiceImpl();
MessengerService stub = (MessengerService) UnicastRemoteObject
  .exportObject((MessengerService) server, 0);

Мы используем статический метод UnicastRemoteObject.exportObject для создания реализации заглушки. Заглушка — это то, что делает волшебство связи с сервером по базовому протоколу RMI.

Первым аргументом exportObject является объект удаленного сервера.

Второй аргумент — это порт, который exportObject использует для экспорта удаленного объекта в реестр.

Нулевое значение означает, что нам все равно, какой порт использует exportObject, что является типичным и выбирается динамически.

К сожалению, метод exportObject() без номера порта устарел.

3.2 Создание реестра

Мы можем создать реестр локально на нашем сервере или как отдельную автономную службу.

Для простоты мы создадим реестр, локальный для нашего сервера:

Registry registry = LocateRegistry.createRegistry(1099);

Это создает реестр, к которому заглушки могут быть привязаны серверами и обнаружены клиентами.

Кроме того, мы использовали метод createRegistry, так как мы создаем реестр локально на сервере.

По умолчанию реестр RMI работает на порту 1099. Вместо этого в фабричном методе createRegistry можно также указать другой порт.

Но в автономном случае мы бы вызвали getRegistry, передав в качестве параметров имя хоста и номер порта.

3.3 Привязка заглушки

Итак, давайте привяжем нашу заглушку к реестру. Реестр RMI — это средство именования, такое как JNDI и т. д. Здесь мы можем следовать аналогичному шаблону, привязывая нашу заглушку к уникальному ключу:

registry.rebind("MessengerService", stub);

В результате удаленный объект теперь доступен любому клиенту, который может найти его. реестр.

4. Создание клиента

Наконец, давайте напишем клиент для вызова удаленных методов.

Для этого мы сначала найдем реестр RMI. Кроме того, мы будем искать заглушку удаленного объекта, используя ограниченный уникальный ключ.

И, наконец, мы вызовем метод sendMessage:

Registry registry = LocateRegistry.getRegistry();
MessengerService server = (MessengerService) registry
  .lookup("MessengerService");
String responseMessage = server.sendMessage("Client Message");
String expectedMessage = "Server Message";
 
assertEquals(expectedMessage, responseMessage);

Поскольку мы запускаем реестр RMI на локальной машине и используем порт по умолчанию 1099, мы не передаем никаких параметров в getRegistry.

Действительно, если реестр находится на другом хосте или другом порту, мы можем указать эти параметры.

Как только мы найдем объект-заглушку с помощью реестра, мы можем вызывать методы на удаленном сервере.

5. Заключение

«В этом руководстве мы получили краткое введение в Java RMI и то, как он может стать основой для клиент-серверных приложений. Следите за дополнительными сообщениями о некоторых уникальных функциях RMI!

Исходный код этого руководства можно найти на GitHub.