Читаем Компьютерные сети. 6-е изд. полностью

Иногда с помощью некоторых уловок все же удается передать указатели. Допустим, первым параметром является указатель на целое число k. Клиентская заглушка может выполнить маршалинг k и передать его серверу. Серверная заглушка создаст указатель на полученную переменную k и передаст его серверной процедуре. Именно этого она и ждет. Когда серверная процедура возвращает управление серверной заглушке, последняя отправляет k обратно клиенту, где обновленное значение этой переменной записывается вместо старого (если оно было изменено сервером). По сути, стандартная последовательность действий, выполняемая при передаче параметра по ссылке, заменилась прямой и обратной передачей копии параметра. Увы, этот трюк не всегда удается применить, в частности, нельзя это сделать, если указатель ссылается на граф или иную сложную структуру данных. Как мы увидим далее, по этой причине на параметры удаленно вызываемых процедур должны накладываться определенные ограничения.

Вторая проблема заключается в том, что в языках со слабой типизацией данных (например, в C) вполне допустимо написание процедуры, которая подсчитывает скалярное произведение двух векторов (массивов), без указания их размеров. Каждая из этих структур в качестве ограничителя имеет какое-то значение, известное только вызывающей и вызываемой процедурам. В этих обстоятельствах клиентская заглушка не способна запаковать параметры: нет никакой возможности определить их размеры.

Третья проблема состоит в том, что не всегда можно распознать типы параметров по спецификации или по самому коду. В качестве примера приведем процедуру printf, у которой может быть любое число параметров (не меньше одного), и они могут представлять собой произвольную смесь различных целочисленных (int, short, long), а также символьных и строковых параметров, чисел различной длины с плавающей запятой и др. Задача удаленного вызова процедуры printf может оказаться практически невыполнимой из-за такой своеобразной толерантности языка С. Однако не существует правила, говорящего, что удаленный вызов процедур возможен, только если используется любой другой язык кроме С (С++). Это подорвало бы репутацию метода RPC среди программистов.

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

Все эти проблемы не означают, что RPC безнадежен. В действительности он широко используется, просто нужны некоторые ограничения для его корректной работы на практике.

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

Проблема более высокого уровня связана с тем, что операция может не быть идемпотентной (то есть ее нельзя повторить без риска сбоя). В простом случае мы имеем дело с идемпотентными операциями, такими как DNS-запросы и ответы. Клиент может повторно передавать такие пакеты сколько угодно, ничем не рискуя, до тех пор пока не придет ответ. Пакет может не дойти до сервера, а ответ может потеряться — это неважно. В любом случае, когда ответ придет, он будет тем же (если, конечно, за это время не обновится база данных DNS). Но не все операции идемпотентны: например, если они обладают важными побочными эффектами вроде изменения счетчика. RPC для таких операций требует более сложной семантики: вызванная процедура не должна выполняться несколько раз. В таких случаях может понадобиться установка TCP-соединения и отправка запроса по TCP, а не по UDP.

6.4.3. Транспортные протоколы реального времени

Перейти на страницу:

Похожие книги