Механизм вызова удаленных процедур - RPC

Программные реализации системы, как правило, поддерживают один или два протокола. Например, система RPC разработки фирмы Sun Microsystems поддерживает передачу сообщений с использованием протоколов TCP и UDP. Выбор того или иного протокола зависит от требований приложения. Выбор протокола UDP оправдан для приложений, обладающих следующими характеристиками: - Вызываемые процедуры идемпотентны - Размер передаваемых аргументов и возвращаемого результата меньше размера пакета UDP - 8 Кбайт. - Сервер обеспечивает работу с несколькими сотнями клиентов. Поскольку при работе с протоколами TCP сервер вынужден поддерживать соединение с каждым из активных клиентов, это занимает значительную часть его ресурсов. Протокол UDP в этом отношении является менее ресурсоемким С другой стороны, TCP обеспечивает эффективную работу приложений со следующими характеристиками: - Приложению требуется надежный протокол передачи - Вызываемые процедуры неидемпонентны - Размер аргументов или возвращаемого результата превышает 8 Кбайт Выбор протокола обычно остается за клиентом, и система по-разному организует формирование и передачу сообщений. Так, при использовании протокола TCP, для которого передаваемые данные представляют собой поток байтов, необходимо отделить сообщения друг от друга. Для этого например, применяется протокол маркировки записей, описанный в RFC1057 "RPC: Remote Procedure Call Protocol specification version 2", при котором в начале каждого сообщения помещается 32-разрядное целое число, определяющее размер сообщения в байтах. По-разному обстоит дело и с семантикой вызова. Например, если RPC выполняется с использованием ненадежного транспортного протокола (UDP), система выполняет повторную передачу сообщения через короткие промежутки времени (тайм-ауты). Если приложение-клиент не получает отклик, то с уверенностью можно сказать, что процедура была выполнена ноль или большее число раз. Если отклик был получен, приложение может сделать вывод, что процедура была выполнена хотя бы однажды. При использовании надежного транспортного протокола (TCP) в случае получения отклика можно сказать, что процедура была выполнена один раз. Если же отклик не получен, определенно сказать, что процедура выполнена не была, нельзя3. Как это работает? По существу, собственно система RPC является встроенной в программу-клиент и программу-сервер. Отрадно, что при разработке распределенных приложений, не придется вникать в подробности протокола RPC или программировать обработку сообщений. Система предполагает существование соответствующей среды разработки, которая значительно облегчает жизнь создателям прикладного программного обеспечения. Одним из ключевых моментов в RPC является то, что разработка распределенного приложения начинается с определения интерфейса объекта - формального описания функций сервера, сделанного на специальном языке. На основании этого интерфейса затем автоматически создаются заглушки клиента и сервера. Единственное, что необходимо сделать после этого, - написать фактический код процедуры. В качестве примера рассмотрим RPC фирмы Sun Microsystems. Система состоит из трех основных частей: - rpcgen(1) - RPC-компилятор, который на основании описания интерфейса удаленной процедуры генерирует заглушки клиента и сервера в виде программ на языке С. - Библиотека XDR (eXternal Data Representation), которая содержит функции для преобразования различных типов данных в машинно-независимый вид, позволяющий производить обмен информацией между разнородными системами. - Библиотека модулей, обеспечивающих работу системы в целом. Рассмотрим пример простейшего распределенного приложения для ведения журнала событий. Клиент при запуске вызывает удаленную процедуру записи сообщения в файл журнала удаленного компьютера. Для этого придется создать как минимум три файла: спецификацию интерфейсов удаленных процедур log.x (на языке описания интерфейса), собственно текст удаленных процедур log.c и текст головной программы клиента main ( ) - client.c (на языке С) . Компилятор rpcgen(l) на основании спецификации log.x создает три файла: текст заглушек клиента и сервера на языке С (log clnt.c и log svc.c) и файл описаний log.h, используемый обеими заглушками. Итак, рассмотрим исходные тексты программ. log.x В этом файле указываются регистрационные параметры удаленной процедуры - номера программы, версии и процедуры, а также определяется интерфейс вызова - входные аргументы и возвращаемые значения. Таким образом, определена процедура RLOG, в качестве аргумента принимающая строку (которая будет записана в журнал), а возвращаемое значение стандартно указывает на успешное или неудачное выполнение заказанной операции.

program LOG_PROG {
version LOG_VER {
int RLOG (string) = 1;
}
= 1;
} = 0х31234567;

Компилятор rpcgen(l) создает файл заголовков log.h, где, в частности, определены процедуры:
log.h

/*

* Please do not edit this file.

* It was generated using rpcgen.

*/

#ifndef _LOG_H_RPCGEN

#define _LOG_H_RPCGEN

#include <rpc/rpc.>

/* Номер программы*/

#define LOG_PROG ((unsigned long) (0х31234567))

#define LOG_VER ((unsigned long) (1)) /*Номер версии*/

#define RLOG ((unsigned long) (1)) /*Номер процедуры*/

extern int *rlog_l () ;

/*Внутренняя процедура - нам ее использовать не придется*/ extern int log_prog_l_freeresult();

#endif /* !_LOG_H_RPCGEN */

Рассмотрим этот файл внимательно. Компилятор транслирует имя RLOG определенное в файле описания интерфейса, в rlog_1, заменяя прописные символы на строчные и добавляя номер версии программы с подчеркиванием. Тип возвращаемого значения изменился с int на int *. Таково правило - RPC позволяет передавать и получать только адреса объявленных при описании интерфейса параметров. Это же правило касается и передаваемой в качестве аргумента строки. Хотя из файла print.h это не следует, на самом деле в качестве аргумента функции rlog_l ( ) также передается адрес строки.
Помимо файла заголовков компилятор rpcgen(l) создает модули заглушки клиента и заглушки сервера. По существу, в тексте этих файлов заключен весь код удаленного вызова.
Заглушка сервера является головной программой, обрабатывающей все сетевое взаимодействие с клиентом (точнее, с его заглушкой). Для выполнения операции заглушка сервера производит локальный вызов функции, текст которой необходимо написать:
log.c
#include
#include
#include
#include "log.h"
int *rlog_1 (char **arg)
{
/*Возвращаемое значение должно определяться как static*/
static int result;
int fd; /*Файловый дескриптор журнала*/
int len;
result = 1;
/*0ткроем файл журнала (создадим, если он не существует), в случае неудачи вернем код ошибки result == 1.*/
if ((fd=open( "./server .log",
O_CREAT | O_RDWR | O_APPEND)) < 0) return (&result);
len = strlen(*arg);
if (write(fd, *arg, strlen(*arg)) != len)
result = 1;
else
result = 0;
close (fd);
return(&result); /*Возвращаем результат - адрес result*/
}
Заглушка клиента принимает аргумент, передаваемый удаленной процедуре, делает необходимые преобразования, формирует запрос на сервер portmap(1M), обменивается данными с сервером удаленной процедуры и, наконец, передает возвращаемое значение клиенту. Для клиента вызов удаленной процедуры сводится к вызову заглушки и ничем не отличается от обычного локального вызова.
client.c
#include
#include "log.h"
main(int argc, char *argv[])
{
CLIENT *cl;
char *server, *mystring, *clnttime;
time_t bintime;
int *result;
if (argc != 2) {
fprintf(stderr, "Формат вызова: %s Адрес_хоста\n",
argv [0]);
exit (1) ;
}
server = argv [1];
/*Получим дескриптор клиента. В случае неудачи - сообщим о
невозможности установления связи с сервером*/
if ( (с1 = clnt_create (server,
LOG_PROG, LOG_VER, "udp")) == NULL) {
clnt_pcreateerror (server);
exit (2);
}
/*Выделим буфер для строки*/
mystring = (char * )malloc (100);
/*Определим время события*/
bintime = time ((time_t * ) NULL);
clnttime = ctime(&bintime);
sprintf (mystring, "%s - Клиент запущен", clnttime);
/*Передадим сообщение для журнала - время начала работы клиента. В случае неудачи - сообщим об ошибке*/
if ( (result = rlog_l(&mystring, cl)) == NULL) {
fprintf(stderr, "error2\n");
clnt_perror(cl, server);
exit(3);
}
/*B случае неудачи на удаленном компьютере сообщим об ошибке*/
if (*result !=0 )
fprintf(stderr, "Ошибка записи в журнал\n");
/*0свободим дескриптор*/
cint destroy(cl);
exit (0);
}
Заглушка клиента log_clnt.c компилируется с модулем client.c для получения исполняемой программы клиента.
cc -о rlog client.c log_clnt.c -Insl
Заглушка сервера log_svc.c и процедура log.c компилируются для получения исполняемой программы сервера.
cc -о logger log_svc.c log.c -Insl
Теперь на некотором хосте server.nowhere.ru необходимо запустить серверный процесс:
$ logger
После чего при запуске клиента rlog на другой машине сервер добавит соответствующую запись в файл журнала.
Схема работы RPC в этом случае приведена на рис. 1. Модули взаимодействуют следующим образом:
1. Когда запускается серверный процесс, он создает сокет UDP и связывает любой локальный порт с этим сокетом. Далее сервер вызывает библиотечную функцию svc_register(3N) для регистрации номеров программы и ее версии. Для этого функция обращается к процессу portmap(IM) и передает требуемые значения. Сервер portmap(IM) обычно запускается при инициализации системы и связывается с некоторым общеизвестным портом. Теперь portmap(3N) знает номер порта для нашей программы и версии. Сервер же ожидает получения запроса. Заметим, что все описанные действия производятся заглушкой сервера, созданной компилятором rpcgen(IM).

Отправить комментарий

Проверка
Антиспам проверка
Image CAPTCHA
...