Э. Таненбаум, М. ван Стеен - Распределённые системы (принципы и парадигмы) (1162619), страница 16
Текст из файла (страница 16)
В основном узлыподдерживают стандартный коммуникационный протокол типа T C P / I P , что делает несложной организацию их совместной работы. Однако здесь могут встретиться трудности с переносом приложений под разные платформы. Распределенные операционные системы в основном рассчитаны не на открытость, а на1.5. Модель клиент-сервер67максимальную производительность, в результате на дороге к открытым системам у них стоит множество запатентованных решений.1.5. Модель клиент-серверДо этого момента мы вряд ли сказали что-то о действительной организации распределенных систем, более интересуясь тем, как в этих системах организованыпроцессы. Несмотря на то что достичь согласия по вопросам, связанным с распределенными системами, было нелегко, по одному из вопросов исследователи и разработчики все же договорились. Они пришли к выводу о том, что мышление впонятиях клиентов, запрашивающих службы с серверов, помогает понять сложность распределенных систем и управляться с ней.
В этом разделе мы кратко рассмотрим модель клиент-сервер.1.5.1. Клиенты и серверыв базовой модели клиент-сервер все процессы в распределенных системах делятся на две возможно перекрывающиеся группы. Процессы, реализующие некоторую службу, например службу файловой системы или базы данных, называютсясерверами {servers).
Процессы, запрашивающие службы у серверов путем посылки запроса и последующего ожидания ответа от сервера, называются клиентами{clients). Взаимодействие клиента и сервера, известное также под названием режим работы запрос-ответ {request-reply behavior), иллюстрирует рис. 1.18.Ожидание результатаКлиент•Сервер •Служба провайдераВремя -Рис. 1.18. Обобщенное взаимодействие между клиентом и серверомЕсли базовая сеть так же надежна, как локальные сети, взаимодействие между клиентом и сервером может быть реализовано посредством простого протокола, не требующего установления соединения. В этом случае клиент, запрашиваяслужбу, облекает свой запрос в форму сообщения с указанием в нем службы, которой он желает воспользоваться, и необходимых для этого исходных данных.Затем сообщение посылается серверу.
Последний, в свою очередь, постоянноожидает входящего сообщения, получив его, обрабатывает, упаковывает результат обработки в ответное сообщение и отправляет его клиенту.Использование не требующего соединения протокола дает существенный выигрыш в эффективности. До тех пор пока сообщения не начнут пропадать илиповреждаться, можно вполне успешно применять протокол типа запрос-ответ.К сожалению, создать протокол, устойчивый к случайным сбоям связи, — петри-68Глава 1. Введениевиальная задача.
Все, что мы можем сделать, — это дать клиенту возможностьповторно послать запрос, на который не был получен ответ. Проблема, однако,состоит в том, что клиент не может определить, действительно ли первоначальное сообш,ение с запросом было потеряно или ошибка произошла при передачеответа. Если потерялся ответ, повторная посылка запроса может привести к повторному выполнению операции. Если операция представляла собой что-то вроде«снять 10 000 долларов с моего банковского счета», понятно, что было бы гораздо лучше, если бы вместо повторного выполнения операции вас просто уведомили о произошедшей ошибке.
С другой стороны, если операция была «сообщитемне, сколько денег у меня осталось», запрос прекрасно можно было бы послатьповторно. Нетрудно заметить, что у этой проблемы нет единого решения. Мы отложим детальное рассмотрение этого вопроса до главы 7.В качестве альтернативы во многих системах клиент-сервер используется надежный протокол с установкой соединения.
Хотя это решение в связи с его относительно низкой производительностью не слишком хорошо подходит для локальных сетей, оно великолепно работает в глобальных системах, для которыхненадежность является «врожденным» свойством соединений. Так, практическивсе прикладные протоколы Интернета основаны на надежных соединениях попротоколу T C P / I P . В этих случаях всякий раз, когда клиент запрашивает службу, до посылки запроса серверу он должен установить с ним соедргнение. Серверобычно использует для посылки ответного сообщения то же самое соединение,после чего оно разрывается. Проблема состоит в том, что установка и разрыв соединения в смысле затрачиваемого времени и ресурсов относительно дороги,особенно если сообщения с запросом и ответом невелики.
Мы обсудим альтернативные решения, в которых управление соединением объединяется с передачейданных, в следующеР! главе.Примеры клиента и сервераЧтобы внести большую ясность в то, как работают клиент и сервер, в этом пунктемы рассмотрим описание клиента и файлового сервера на языке С. И клиент,и сервер должны совместно использовать некоторые определения, которые мысоберем вместе в файл под названием header.h, текст которого приведен в листинге 1.3. Эти определения затем включаются в тексты программ клиента и сервера следующей строкой:#1nclucle <header.h>Эта инструкция вызовет активность препроцессора, который посимвольновставит все содержимое файла header.h в текст программы до того, как начнетсяее компиляция.Листинг 1.3.
Файл header.h, используемый клиентом и сервером/ * Определения, необходимые и клиентам, и серверам * /#clefine TRUE1/* Максимальная длина имени файла */#define МАХ_РАТН255/* Максимальное количество данных, передаваемое за один раз */1.5. Модель клиент-сервер69#def1ne BUF_SIZE1024/* Сетевой адрес илового сервера */#def1ne FILE SERVER243/* Определения разрешенных операций *//* создать новый файл */#define CREATE1/* считать данные из файла и вернуть их */#define READ2/* записать данные в файл '^1#define WRITE3/* удалить существующий файл */#def1ne DELETE4/* Коды ошибок *//^ операция прошла успешно */#def1ne OKО/* запрос неизвестной операции */#def1ne E_BAD_OPER-1/* ошибка в параметре */#def1ne E_BAD_PARAM-2/* ошибка диска или другая ошибка чтения-записи */#def1ne Е 10-3/* Определение формата сообщения */struct message {long source/* идентификатор источника */long dest;/* идентификатор приемника '^1long opcode/* запрашиваемая операция */long count;/* число передаваемых байт */long offset/* позиция в файле, с которой начинаетсяввод-вывод *//^ результат операции */long result:/* имя файла, с которым производятсяchar name[MAX_PATH];операции */char data[BUF SIZE]:данные, которые будут считаны илизаписаны */}:Итак, перед нами текст файла header.h.
Он начинается с определения двухконстант, МАХ_РАТН и BUF_SIZE, которые определяют размер двух массивов, используемых в сообщении. Первая задает число символов, которое может содержатьсяв имени файла (то есть в строке с путем типа /usr/ast/books/opsys/chapter1.t).Вторая задает размер блока данных, который может быть прочитан или записанза одну операцию путем установки размера буфера. Следующая константа, FILE_SERVER, задает сетевой адрес файлового сервера, на который клиенты могут посылать сообщения.Вторая группа констант задает номера операций. Они необходимы для того,чтобы и клиент, и сервер знали, какой код представляет чтение, какой код — запись и т.
д. Мы приводим здесь только четыре константы, в реальных системахих обычно больше.70Глава 1. ВведениеКаждый ответ содержит код результата. Если операция завершена успешно,код результата обычно содержит полезную информацию (например, реальноечисло считанных байтов). Если нет необходимости возвращать значение (например, при создании файла), используется значение ОК. Если операция по какимлибо причинам окончилась неудачей, код результата (E_BAD_OPER, E_BAD_PARAM и др.)сообщает нам, почему это произошло.Наконец, мы добрались до наиболее важной части файла header.h — определения формата сообщения.
В нашем примере это структура с 8 полями. Этот формат используется во всех запросах клиентов к серверу и ответах сервера. В реальных системах, вероятно, фиксированного формата у сообщений не будет (поскольку не во всех случаях нужны все поля), но здесь это упростит объяснение.Поля source и dest определяют, соответственно, отправителя и получателя. Полеopcode — это одна из ранее определенных операций, то есть create, read, write илиdelete.