16+
ComputerPrice
НА ГЛАВНУЮ СТАТЬИ НОВОСТИ О НАС




Яндекс цитирования


Версия для печати

Модуль поиска не установлен.

Удаленный вызов процедур (RPC) и вызов удаленных методов (RMI)

19.08.2003

Антон Тульчинский <antontul@rambler.ru>

Введение

Идея вызова удаленных процедур (Remote Procedure Call - RPC) состоит в расширении хорошо известного и понятного механизма передачи управления и данных внутри программы, выполняющейся на одной машине, на передачу управления и данных через сеть. То есть, клиентское приложение обращается к процедурам, хранящимся на сервере. Средства удаленного вызова процедур предназначены для облегчения организации распределенных вычислений. Наибольшая эффективность использования RPC достигается в тех приложениях, в которых существует интерактивная связь между удаленными компонентами с небольшим временем ответов и относительно малым количеством передаваемых данных. Такие приложения называются RPC-ориентированными.

Характерными чертами RPC являются:

- асимметричность, то есть одна из взаимодействующих сторон является инициатором;

- синхронность, то есть выполнение вызывающей процедуры приостанавливается с момента выдачи запроса и возобновляется только после возврата из вызываемой процедуры.

Существует несколько реализаций процедур удаленного вызова в различных операционных системах. В операционной системе UNIX используется процедура под одноименным названием (Remote Procedure Call - RPC). Данная процедура внедрена в ядро системы. Ее выполнение обеспечивается протоколом RPC. В операционных системах Windows удаленный вызов процедур начал развиваться на базе механизмов OLE, которые постепенно развились в технологию DCOM (Distributed Component Object Model). Данная технология позволяет создавать достаточно мощные распределенные сетевые вычислительные среды. В технологии используются фирменные протоколы Microsoft.

Механизм работы RPC

Перед непосредственным вызовом на клиентской и серверной стороне должны быть созданы специальные структуры (процедуры, файлы) - это так называемые клиентский стаб (stub) и серверный скелетон (skeleton), которые необходимы для корректной работы RPC. Чаще всего, они генерируются автоматически специальными утилитами по основному коду программы.

При удаленном вызове процедуры в распределенной системе происходят следующие действия:

1. Процедура клиента вызывает стаб как обычную процедуру. Стаб упаковывает параметры (маршализация, marshaling).

2. Стаб обращается к ядру ОС.

3. Ядро посылает сообщение на удаленную машину (ядру удаленного ПК).

4. Передача полученного сообщения скелетону серверного процесса.

5. Распаковка параметров (демаршализация, unmarshaling). Вызов требуемой процедуры.

6. Процедура на сервере выполняется. Возвращает результаты скелетону.

7. Скелетон упаковывает результат.

8. Передача результата ядру.

9. Ядро сервера передает сообщение по сети ядру клиента.

10. Ядро клиента обращается к стабу. Стаб распаковывает полученный результат.

11. Передача от стаба клиентскому процессу.

Служба "Удаленный вызов процедур (RPC)" в ОС Windows

Для того чтобы понять важность механизма удаленного вызова процедур, можно рассмотреть хотя бы список утилит и служб, которые не работают без RPC в ОС Windows 2000. Фактически, отключение службы RPC в указанной среде приводит к краху всей системы. Итак, от службы "Удаленный вызов процедур (RPC)" зависят:

1. Telnet - позволяет удаленному пользователю войти в систему и запустить программы консоли с помощью командной строки.

2. Windows Installer - устанавливает, удаляет или восстанавливает программное обеспечение в соответствии с инструкциями файлов MSI.

3. Агент политики IPSEC - управляет политикой IP-безопасности и запускает ISAKMP/Oakley (IKE) и драйвер IP-безопасности.

4. Диспетчер очереди печати - загружает в память файлы для последующей печати.

5. Защищенное хранилище - обеспечивает защищенное хранение секретных данных, таких как закрытые ключи, для предотвращения несанкционированного доступа служб, процессов или пользователей.

6. Инструментарий управления Windows - предоставляет информацию об управлении системой.

7. Клиент отслеживания изменившихся связей - посылает оповещения о файлах, перемещенных между томами NTFS в сетевом домене.

8. Координатор распределенных транзакций - координация транзакций, распределенных по нескольким базам данных, очередям сообщений, файловым системам или другим защищенным диспетчерам ресурсов транзакций.

9. Маршрутизация и удаленный доступ - предлагает услуги маршрутизации организациям в локальной и глобальной сетях.

10. Планировщик заданий - позволяет выполнять программы в назначенное время.

11. Сетевые подключения - управляет объектами папки "Сеть и удаленный доступ к сети", отображающей свойства локальной сети и подключений удаленного доступа.

12. Система событий COM+ - автоматическое распространение событий подписавшимся компонентам COM.

13. Служба индексирования - индексирование для быстрого поиска.

14. Служба сообщений - посылает и получает сообщения, переданные администраторами или службой оповещений.

15. Служба факсов - помогает отправлять и принимать факсимильные сообщения.

16. Съемные ЗУ - управляет съемными носителями, дисками и библиотеками.

17. Телефония - обеспечивает поддержку Telephony API (TAPI) для программ, управляющих телефонным оборудованием и голосовыми IP-подключениями на этом компьютере, а также через ЛВС - на серверах, где запущена соответствующая служба.

RMI-приложения

Вызов Удаленных Методов Remote Method Invocation (RMI) является реализацией идей RPC для языка программирования Java.

RMI - продукт компании JavaSoft, разработанный для Java и интегрированный в JDK 1.1 и выше. RMI реализует распределенную модель вычислений и обеспечивает средства коммуникации между Java-программами (виртуальными Java-машинами), выполняющимися на одном или нескольких удаленных компьютерах. RMI позволяет клиентским и серверным приложениям через сеть вызывать методы клиентов/серверов, выполняющихся на виртуальных Java-машинах. Основное преимущество RMI заключается в том, что он предоставляет программисту программируемый интерфейс более высокого уровня, который позволяет передавать ссылку на удаленный объект в качестве аргумента или возвращать ее в качестве результата. RMI требует, чтобы на обоих концах соединения выполнялись Java-программы. Сетевое соединение достигается с использованием TCP/IP-протокола. Архитектура RMI приведена на рис. "Архитектура RMI".

Client Stub (переходник для клиента - некая сущность на клиенте, которая обеспечивает функции приема/передачи), и Server Skeleton (переходник для сервера - некая сущность на сервере, которая обрабатывает удаленные вызовы) порождены от общего интерфейса, но различаются тем, что Client Stub служит просто для подсоединения к RMI Registry, а Server Stub используется для связи непосредственно с функциями сервера.

RMI является в действительности новым видом брокера объектных запросов, который строится на объектной модели Java. Как и ORB, RMI вводит пять ключевых моментов:

1. Позволяет перемещать код в дополнение к данным.

2. Практически обеспечивает безопасность выполнения загружаемого кода.

3. Позволяет передавать объекты по значению.

4. Использует Java как язык определения интерфейса и как язык реализации.

5. Использует именующую схему на базе унифицированного указателя ресурсов Uniform Resource Locator (URL).

При этом производится преобразование объектов в последовательную форму - в поток байтов, передаваемых как параметр в сообщении с помощью протокола TCP/IP.

Интерфейсы RMI можно разделить на 4 категории:

- ядро RMI - определяет интерфейсы, необходимые для выполнения вызовов удаленных методов;

- служба именования RMI - определяет интерфейсы и классы, позволяющие получить ссылки на серверные объекты по имени;

- безопасность RMI - определяет новый менеджер безопасности RMI и интерфейсы загрузчика классов (RMI расширяет механизм загрузки классов Java по требованию на загрузку стаба);

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

JavaSoft и OMG работают над сближением объектных моделей RMI и CORBA. Это сближение происходит в двух областях:

- RMI через IIOP. JavaSoft разрабатывает версию RMI, которая работает поверх транспорта IIOP. IIOP предоставляет следующие преимущества для RMI:

1. Встроенную поддержку для распространения транзакций.

2. Поддержку брандмауэра на основе ORB с помощью заместителя IIOP (без HTTP-туннелирования).

3. Взаимодействие с объектами, написанными на других языках через подмножество RMI/IDL.

4. Открытый стандарт распределенных объектов.

- RMI/IDL. Стандарт CORBA Java в IDL является стандартом сближения CORBA/RMI. Он позволяет программистам Java определять интерфейсы CORBA c помощью семантики Java RMI вместо CORBA IDL. Компилятор использует эту семантику для автоматического создания CORBA IDL, стабов и скелетонов. Подмножество RMI/IDL позволяет программам RMI вызываться многоязычными клиентами CORBA с помощью IIOP; он также позволяет RMI-программам вызывать объекты CORBA, написанные на других языках.

RMI через IIOP кажется хорошим решением для системы CORBA/Java, поскольку объединяет две мощные технологии. Основным достоинством RMI является то, что с его помощью можно наиболее быстро и просто создать небольшую распределенную систему в чисто Java-среде. Основным недостатком RMI является невозможность интегрирования этого механизма с существующими приложениями.

Сравнение распределенных и нераспределенных Java-программ

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

Интерфейсы в RMI

Архитектура RMI основана на одном важном принципе: определение поведения и реализация этого поведения считаются разными понятиями. RMI дает возможность разделить и выполнить на разных JVM код, определяющий поведение, и код, реализующий поведение.

Это соответствует требованиям распределенных систем, в которых клиенты знают об определениях служб, а серверы предоставляют эти службы. Конкретно в RMI определение удаленной службы кодируется при помощи интерфейса Java. Реализация удаленной службы кодируется в классе. Таким образом, ключ к пониманию RMI - помнить, что интерфейсы определяют поведение, а классы определяют реализацию.

Помните, что интерфейсы Java не содержат исполняемого кода. RMI поддерживает два класса, реализующих один и тот же интерфейс. Первый класс является реализацией поведения и исполняется на сервере. Второй класс работает как промежуточный интерфейс для удаленной службы и исполняется на клиентской машине.

Клиентская программа вызывает методы прокси-объекта, RMI передает запрос на удаленную JVM и направляет его в реализацию объекта. Любые возвращаемые из реализации значения передаются назад в прокси-объект и затем в клиентскую программу.

Уровни архитектуры RMI

Реализация RMI, по существу, состоит из трех абстрактных уровней. Первый - это уровень заглушки и скелета, расположенный непосредственно перед разработчиком. Этот уровень перехватывает вызовы методов, произведенные клиентом при помощи переменной-ссылки на интерфейс, и переадресует их в удаленную службу RMI.

Следующий уровень - уровень удаленной ссылки. Этот уровень понимает, как интерпретировать и управлять ссылками на удаленные объекты служб. В JDK 1.1 этот уровень соединяет клиентов с удаленными объектами служб, которые исполняются на сервере. Это соединение является связью типа один к одному (однонаправленное соединение). В Java 2 SDK этот уровень был расширен поддержкой активации пассивных удаленных объектов при помощи технологии Remote Object Activation.

Транспортный уровень основан на соединениях TCP/IP между сетевыми машинами. Он обеспечивает основные возможности соединения и некоторые стратегии защиты от несанкционированного доступа. При использовании уровневой архитектуры каждый из уровней может быть изменен или заменен без воздействия на остальную систему. Например, транспортный уровень может быть заменен протоколом UDP/IP без изменения остальных уровней.

Поиск удаленных объектов

При рассмотрении архитектуры RMI возникает вопрос: "Как клиент находит удаленную службу RMI?". Клиенты находят удаленные службы, используя службу имен или каталогов. Как клиент может найти службу, используя службу? Но это действительно так. Служба имен или каталогов исполняется на хорошо известном хосте и имеет известный номер порта (хорошо известный означает, что все в организации знают об этом).

RMI может использовать много различных служб каталогов, включая Java Naming and Directory Interface (JNDI). RMI и сама включает в себя простую службу, называемую реестром RMI, rmiregistry. Реестр RMI работает на каждой машине, содержащей объекты удаленных служб и принимающей запросы на обслуживание, по умолчанию используя порт 1099. На хосте программа сервера создает удаленную службу, предварительно создавая локальный объект, реализующий эту службу. Затем она экспортирует этот объект в RMI. Как только объект экспортирован, RMI создает службу прослушивания, ожидающую соединения с клиентом и запроса службы. После экспорта, сервер регистрирует объект в реестре RMI, используя общедоступное имя.

На стороне клиента к реестру RMI доступ обеспечивается через статический класс Naming. Он предоставляет метод lookup(), который клиент использует для запросов к реестру. Метод lookup() принимает URL, указывающий на имя хоста и имя требуемой службы. Метод возвращает удаленную ссылку на обслуживающий объект. URL принимает следующий вид:

rmi://<host_name> [:<name_service_port>] /<service_name>
где host_name - это имя, распознаваемое в локальной сети (LAN), или DNS-имя в сети Internet. Необходимо только указать name_service_port, если служба имен исполняется на порте, отличном от принимаемого по умолчанию 1099.

Использование RMI

Рабочая RMI-система состоит из нескольких частей: определение интерфейсов для удаленных служб, реализация удаленных служб, файлы заглушки и скелета, сервер, предоставляющий удаленные службы, служба имен RMI, дающая возможность клиентам найти удаленные службы, поставщик файла классов (HTTP или FTP-сервер), клиентская программа, которая нуждается в удаленных службах.

Если предположить, что RMI-система уже спроектирована, для ее создания необходимо выполнить следующие шаги:

1. Написать и откомпилировать Java-код для интерфейсов.

2. Написать и откомпилировать Java-код для классов реализации.

3. Создать файлы классов заглушки и скелета из классов реализации.

4. Написать Java-код программы хоста для удаленного обслуживания.

5. Разработать Java-код для клиентской программы RMI.

6. Установить и запустить RMI-систему.

Пример RMI - приложения

Первым шагом является написание и компилирование Java-кода для интерфейсов служб. Интерфейс Calculator определяет все удаленные возможности, предлагаемые службой:

public interface Calculator extends java.rmi.Remote {
public long add(long a, long b) throws java.rmi.RemoteException;
public long sub(long a, long b) throws java.rmi.RemoteException;
public long mul(long a, long b) throws java.rmi.RemoteException;
public long div(long a, long b) throws java.rmi.RemoteException;
}

Обратите внимание, что этот интерфейс расширяет интерфейс Remote, и в сигнатуре каждого метода определяется, что он может генерировать объект RemoteException. Вообще, объект называется удаленным, если он реализует интерфейс Remote. "Реализует" в смысле заголовка (public interface Calculator extends java.rmi.Remote), никаких методов в этом интерфейсе нет. Это - метка. Теперь необходимо написать реализацию удаленной службы. Ниже приведен класс CalculatorImpl:

public class CalculatorImpl extends java.rmi.server.UnicastRemoteObject
implements Calculator {
// Реализации должны иметь явный конструктор для того, чтобы объявить
// исключительную ситуацию RemoteException
public CalculatorImpl()
throws java.rmi.RemoteException {
super();
}
public long add(long a, long b) throws java.rmi.RemoteException {
return a + b;
}
public long sub(long a, long b) throws java.rmi.RemoteException {
return a - b;
}
public long mul(long a, long b) throws java.rmi.RemoteException {
return a * b;
}
public long div(long a, long b) throws java.rmi.RemoteException {
return a / b;
}
}

Класс реализации использует Unicast RemoteObject для присоединения к системе RMI. В данном примере класс реализации непосредственно расширяет UnicastRemoteObject. Это не является обязательным требованием. Класс, не расширяющий UnicastRemoteObject, может использовать свой метод exportObject() для присоединения к RMI. Если класс расширяет UnicastRemoteObject, он должен обеспечить конструктор, объявляющий, что он может сгенерировать объект RemoteException. Если этот конструктор вызывает метод super(), он активизирует код в UnicastRemoteObject, который выполняет RMI-соединение и инициализацию удаленного объекта.

Дальше следует при помощи компилятора RMI (rmic) сгенерировать файлы заглушки и скелета. Компилятор запускается с указанием файла класса, реализующего удаленную службу ( "rmic CalculatorImpl").

Удаленные службы RMI должны быть помещены в процесс сервера. Класс CalculatorServer является очень простым сервером, предоставляющим простые элементы для размещения.

import java.rmi.Naming;

public class CalculatorServer {
public CalculatorServer() {
try {
Calculator c = new CalculatorImpl();
Naming.rebind("
rmi://localhost:1099/
CalculatorService", c);
} catch (Exception e) {
System.out.println("Trouble: " + e);
}
}
public static void main(String args[]) {
new CalculatorServer();
}
}

Исходный код клиента, к примеру, может быть следующий:

import java.rmi.Naming;
import java.rmi.RemoteException;
import java.net.MalformedURLException;
import java.rmi.NotBoundException;
public class CalculatorClient {
public static void main(String[] args) {
try {
Calculator c = (Calculator)
Naming.lookup(
"rmi://remotehost
/CalculatorService");
System.out.println( c.sub(4, 3) );
System.out.println( c.add(4, 5) );
System.out.println( c.mul(3, 6) );
System.out.println( c.div(9, 3) );
}
catch (MalformedURLException murle) {
System.out.println();
System.out.println(
"MalformedURLException");
System.out.println(murle);
}
catch (RemoteException re) {
System.out.println();
System.out.println(
"RemoteException");
System.out.println(re);
}
catch (NotBoundException nbe) {
System.out.println();
System.out.println(
"NotBoundException");
System.out.println(nbe);
}
catch (
java.lang.ArithmeticException
ae) {
System.out.println();
System.out.println(
"java.lang.ArithmeticException");
System.out.println(ae);
}
}
}

Теперь можно запускать систему. Сделать это можно (после получения соответствующих class-файлов и размещения их на одном или разных ПК) так:

1. Запустить реестр RMI ( "rmiregistry" ).

2. Запустить сервер ("java CalculatorServer").

3. Запустить клиент ("java CalculatorClient").

Если все пройдет хорошо, вы увидите следующую информацию:

1
9
18
3

Вот и все - работающая система RMI готова. Даже если вы запустили три консоли на одном и том же компьютере, RMI использует стек протоколов TCP/IP вашей сети для взаимодействия между тремя отдельными JVM. Это вполне законченная RMI-система.

Распространение классов RMI

Для запуска RMI-приложения файлы поддерживающих классов должны быть расположены в таких местах, где бы они могли быть найдены сервером и клиентами.

Для сервера должны быть доступны (для загрузчика классов) классы:

- Определения интерфейса удаленной службы

- Реализации удаленных служб

- Скелеты для классов реализации (только для серверов, основанных на JDK 1.1)

- Заглушки для классов реализации

- Все остальные классы сервера

Для клиента должны быть доступны (для загрузчика классов) классы:

- Определения интерфейса удаленной службы

- Заглушки для классов, реализующих удаленную службу

- Классы сервера для объектов, используемых клиентом (таких, как возвращаемое значение)

- Все остальные классы клиента

Если вы знаете, какие файлы должны быть размещены на различных узлах сети, то сделать их доступными для каждого загрузчика классов JVM не составит труда.

Распределенная сборка мусора

Одним из преимуществ программирования для платформы Java является отсутствие беспокойства о распределении памяти. JVM имеет автоматический сборщик мусора, который освобождает память, занимаемую любым объектом, который больше не используется исполняющейся программой. Одним из требований к разработке RMI была ее бесшовная интеграция в язык программирования Java, включая и сборку мусора. Разработка эффективного сборщика мусора для одной машины является тяжелой задачей; разработка распределенного сборщика мусора является очень тяжелой задачей. RMI-система обеспечивает подсчитывающий ссылки алгоритм распределенной сборки мусора, основанный на сетевых объектах, используемых в Modula-3. Эта система при работе следит за тем, какие клиенты запросили доступ к удаленным объектам, выполняющимся на сервере. Когда появляется ссылка, сервер помечает объект как "грязный", а когда клиент удаляет ссылку, объект помечается как "чистый".

Интерфейс к DGC (распределенный сборщик мусора) скрыт на уровне заглушек и скелетов. Однако удаленный объект может реализовать интерфейс java.rmi.server.Unreferenced и получить уведомление через метод unreferenced, когда нет больше ни одного клиента, содержащего живую ссылку. В дополнение к механизму подсчета ссылок живая ссылка в клиенте имеет срок аренды с указанным временем. Если клиент не обновляет соединение к удаленному объекту до истечения срока аренды, ссылка считается мертвой и удаленный объект может быть утилизирован сборщиком мусора. Время аренды управляется системным свойством java.rmi.dgc.leaseValue. Его значение указывается в миллисекундах и по умолчанию равно 10 минутам. Из-за такой семантики сборки мусора, клиент должен быть подготовлен для работы с объектами, которые могут "исчезать".

Заключение

Технология Remote Method Invocation (RMI), впервые представленная в JDK 1.1, продвинула сетевое программирование на более высокий уровень. Хотя RMI относительно проста в использовании и не лишена недостатков, она является необыкновенно мощной технологией и раскрывает перед обычным Java-программистом полностью новую парадигму - мир распределенных объектных вычислений.



статьи
статьи
 / 
новости
новости
 / 
контакты
контакты