Главная страница Взаимодействие нетривиальных процессов Листинг 16.10 (продолжение) 19 printfCresult: *ld\n . outp->resl): 20 clnt destroy(cl): 21 pauseO: 22 exitCO): 23 } После запуска получим ожидаемый результат: Solaris X client kalae 5 result: 25 result: 100 программа в состоянии ожидания, пока мы не завершим ее вручную Однако проверить наши предыдущие утверждения можно лишь с помощью результатов работы профаммы tcpdump. Она показывает, что создается одно соединение по TCP (вызовом с1 ntcreate) и оно используется для обоих запросов клиента. Соединение завершается вызовом clnt destroy, хотя клиент при этом и не завершает свою работу. Идентификатор транзакций Другая часть стратегии тайм-аутов и повторных передач заключается в использовании идентификаторов транзакций (transaction ID или XID) для распознавания запросов 1слиента и ответов сервера. Когда клиент вызывает функцию RPC, библиотека присваивает этому вызову 32-разрядный целочисленный номер и это значение отсылается в запросе RPC. Сервер должен добавить к своему ответу этот номер. При повторной отсылке запроса идентификатор не меняется. Служит он двум целям: 1. Клиент проверяет, что XID ответа совпадает с XID запроса. Если совпадения нет, ответ игнорируется. Если используется протокол TCP, у клиента практически нет шансов получить ответ с неправильным идентификатором, но при использовании протокола UDP поверх плохой сети вероятность получения неправильного XID достаточно высока. 2. Серверу разрешается помещать отсылаемые ответы в кэш, и для проверки идентичности ответов используется, в частности, именно XID. Об этом мы вскоре расскажем. Пакет TI-RPC использует определенный алгоритм вычисления XID для нового запроса. Алгоритм этот описан ниже. Значок * означает побитовую операцию XOR (исключающее ИЛИ): struct timeval now: gettimeofday(&now. NULL): xid = getpidO * now.tv sec * now.tv usec: Кэш повторных ответов Для включения поддержки кэша повторных ответов в библиотеке RPC сервер должен вызвать функцию svc dg enablecache. После включения кэша вьпслючить его нельзя, можно только запустить процесс заново: #include <rpc/rpc.h> int svc dg enablecache(SVCXPRT *xprt. unsigned long size): /* Возвращает 1 в случае успешного завершения. О - в случае ошибки */ Здесь xprt представляет собой транспортный дескриптор, являющийся полем структуры svc req (раздел 16.4). Адрес этой структуры является аргументом процедуры сервера. Размер определяет количество записей в выделяемом кэще. Итак, эта функция включает поддержку кэширования всех отсылаемых ответов в очереди размером size записей. Каждый ответ однозначно определяется следующими параметрами: Ш номером программы; Ш номером версии; Ш номером процедуры; И XID; И адресом клиента (IP-адрес -I- порт UDP). При получении запроса клиента библиотека RPC ищет в кэше ответ на такой запрос. В случае его наличия ответ отсылается клиенту без повторного вызова процедуры сервера. Цель использования кэша повторных ответов состоит в том, чтобы не нужно было вызывать процедуру сервера несколько раз при получении нескольких копий запроса клиента. Это может быть нужно в слзд1ае, если процедура неидемпо-тентна. Повторный запрос может быть ползд1ен из-за того, что ответ был утерян или у клиента время ожидания меньше, чем время передачи ответа по сети. Обратите внимание, что этот кэш действует только для протоколов, работающих с дейтаграммами (таких, как UDP), поскольку при использовании TCP повторный запрос никогда не может быть ползд1ен приложением - он будет обработан TCP (см. упражнение 16.6). 16.6. Семантика вызовов в листинге 15.24 мы привели пример клиента интерфейса дверей, повторно отсылавшего запрос на сервер при прерывании вызова doorcal 1 перехватываемым сигналом. Затем мы показали, что при этом процедура сервера вызывается дважды, а не однократно. Потом мы разделили процедуры сервера на две группы: идем-потентные, которые могут быть вызваны произвольное количество раз без возникновения ошибок, и неидемпотентные, наподобие вычитания определенной суммы из банковского счета. Вызовы процедур могут быть разбиты на три группы: 1. Ровно один раз означает, что процедура была выполнена только один раз. Такого трудно достичь ввиду ненулевой вероятности сбоев в работе сервера. 2. Не более одного раза означает, что процедура вовсе не была выполнена или что она была выполнена один раз. Если вызвавшему процессу возвращается результат, мы знаем, что процедура была выполнена. Если процессу возвращается сообщение об ошибке, мы не знаем, была ли процедура выполнена хотя бы один раз или не была выполнена вовсе. 3. По крайней мере один раз означает, что процедура была выполнена один раз, а возможно, и больше. Это не вызывает проблем для идемпотентных процедур - клиент продолжает передавать запросы до тех пор, пока не ползд1ит правильный ответ. Однако если клиент отправит несколько запросов, существует вероятность, что процедура будет выполнена больше одного раза. При возвращении из локальной процедуры мы можем быть уверены, что она была выполнена ровно один раз. Однако если процесс завершает работу после вызова процедуры, мы не знаем, успела она выполниться или нет. Для удаленных вызовов процедур возможно несколько ситуаций. Если используется протокол TCP и ползд1ен ответ, мы можем быть уверены, что удаленная процедура была вызвана ровно один раз. Однако если ответ не был ползд1ен (сервер вышел из строя), мы уже не можем сказать, была процедура выполнена или нет. Обеспечение семантики ровно один раз при учете возможности досрочного завершения работы сервера и неполадок в сети требует системы обработки транзакций, что лежит за границами возможностей RPC. i Если используется UDP без серверного кэша и был получен ответ, мы можем быть уверены, что процедура была вызвана по крайней мере один раз, но возможно, и несколько. Ii Если используется UDP с серверным кэшем и был получен ответ, мы можем быть уверены, что процедура была вызвана ровно один раз. Однако если ответ не был ползд1ен, мы оказывается в ситуации не более одного раза аналогично сценарию с TCP. ш Если вы стоите перед выбором: □ TCP, □ UDP с кэшем повторных ответов, □ UDP без кэша повторных ответов - мы можем порекомендовать следующее: □ всегда используйте TCP, если только для приложения не важны накладные расходы на обеспечение надежности; □ используйте систему обработки транзакций для неидемпотентных процедур, корректное выполнение которых важно (работа с банковскими счетами, бронирование авиабилетов и т. п.); □ для неидемпотентных процедур использование TCP предпочтительно по сравнению с UDP и кэшем, поскольку TCP был изначально ориентирован на надежность, а добавление кэша к приложению, использующему UDP, вряд ли даст то же самое, что и использование TCP (см., например, раздел 20.5 [24]); □ для идемпотентных процедур можно использовать UDP без кэша; □ для неидемпотентных процедур использование UDP без кэша опасно. В следующем разделе будут рассмотрены дополнительные преимущества использования TCP.
|
© 2000 - 2024 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования. |