Ордена ленина институт прикладной математики им. М. В. Келдыша Российской Академии Наук

Вид материалаДокументы
1.Режимы клиент-серверного взаимодействия и их особенности
Подобный материал:
1   2   3   4   5   6

1.Режимы клиент-серверного взаимодействия и их особенности



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

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

Очень поучительным в этой связи и, одновременно вызвавшим большие нарекания со стороны пользователей, моментом стал период выхода в Интернет клиент-серверных решений, ранее ориентировавшихся на работу в условиях локальных сетей. Зачастую, вся адаптация этих систем к Интернет сводилась к реализации соответствующего прикладного протокола в стеке TCP/IP, но при этом оставался неизменным традиционно принятый в локальных сетях синхронный режим доступа к серверу (например, к файловому серверу или серверу баз данных). Этот период запомнился появлением большого количества клиентских приложений, «умирающих» после обращения по глобальной сети. Для исправления ситуации потребовались немалые усилия – пока в дизайне приложений не стали, как обязательные элементы, появляться кнопки «Stop» и индикаторы прогресса, а разработчики не научились азам использования асинхронных вызовов и многопоточности для предотвращения блокировки пользовательского интерфейса во время длительных операций клиент-серверного обмена .

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

Таким образом, при том разделении задач между программными составляющими броузера и Веб-приложения, которое принято в классической модели, броузер принимает на себя роль посредника между пользователем и приложением, размещенным в сети, и гарантирует определенное качество, управляемость работы приложения. При этом все проблемы, возникающие в связи с обеспечением асинхронного режима доступа к Интернет, относятся к сфере ответственности разработчиков программного обеспечения броузера. Например, именно им приходится «ломать голову» над тем, какую часть страницы из того, что уже получено с сервера, можно отобразить на экране, а какую – пока рано. Асинхронность также отражена и в самом дизайне Веб-броузера, где в частности предусмотрена кнопка Stop (чтобы пользователь мог в любой момент остановить загрузку очередной страницы), кнопки Back и Forward (чтобы можно было в любой момент перейти на предыдущую или следующую из просмотренных страниц) или, наконец, кнопка [X] в верхнем правом углу окна броузера (чтобы просто штатным образом закрыть приложение не дожидаясь пока завершится очередное соединение с сервером).

Приложения нового поколения Web 2.0, обладающие насыщенным пользовательским интерфейсом, основаны на совершенно иных принципах работы. Например, при реализации интерфейса типа single page interface (описанного в [17]) головная страница, загруженная в окно броузера, принимает на себя все функции полноценного клиентского приложения. Она может многократно в ходе своего жизненного цикла запрашивать с сервера нужные ей данные, порождать дочерние окна (вложенные, диалоговые, всплывающие и т.п.). При реализации таких приложений становится возможным выбирать режим доступа к серверу – синхронный или асинхронный. Таким образом разработчик подобных приложений принимает на себя ответственность за обеспечение новых требований к пользовательскому интерфейсу, которые раньше в классической схеме Веб-приложения являлись исключительной прерогативой броузера.

Для иллюстрации этих особенностей и возможностей воспользуемся следующим несложным примером. Предположим, что надо реализовать форму (qry_report_from) для задания параметров некоторого отчета, в котором должны быть представлены данные, выбираемые по интервалу календарных дат. Такая форма будет иметь два поля для ввода начальной (from_date) и конечной (to_date) даты, а также ряд дополнительных полей для задания параметров отчета (например, поле «Тип отчета», позволяющее выбрать краткую или полную форму отчета) и кнопку «Получить отчет». Кроме того, что дату можно непосредственно ввести в поле с клавиатуры, это можно сделать и через календарь. Для вызова календаря рядом с полями для задания дат есть кнопки from_date_button и to_date_button, представленные на следующем рисунке.





В настоящее время в Интернет можно найти достаточное количество различных реализаций календаря (например, в виде " onclick="return false">
  • устанавливать связь с полем, в которое будет помещена выбранная в календаре дата;
  • позиционировать себя рядом с полем даты
  • при открытии выставляться на дату, указанную в поле дату либо на текущую дату, если поле пусто;
  • автоматически прятать себя, когда теряется фокус ввода (поэтому нам не надо будет заботиться о том, чтобы вовремя закрывать календарь - надо только показать его, например, по клику мыши на соответствующей кнопке).



Конечно самый простой вариант реализации данного примера – это включить компоненту-календарь непосредственно в форму, сделав ее невидимой в момент загрузки страницы. Тогда достаточно определить обработчики событий (onclick) нажатия кнопок from_date_button и to_date_button как:











// аналогично





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

Для того, чтобы избавить реализацию от подобных недостатков, компонента должна загружаться с сервера в асинхронном режиме. Вообще говоря, данный режим является основным, принятым в современных стандартных Веб-клиентах, способом обращения к серверу. Причем, программистам для работы в асинхронном режиме не требуется решать каких-то запредельно сложных задач, подобных обслуживанию сетевого взаимодействия на уровне сокетов TCP/IP. В равной мере, в тексте программ не надо выделять критические секции, поскольку модель исполнения программного кода, написанного на языке сценариев (" onclick="return false">
Однако сказанное не означает, что в среде Веб-клиента вообще не приходится сталкиваться с трудностями реализации в асинхронном режиме. Так, например, следует учитывать, что отправленный на сервер запрос не исполняется немедленно. В этой связи, приложение может воспользоваться поступающими с сервера результатами только спустя какой-то промежуток времени, и в этот период оно должно поддержать какое-то разумное поведение, отличное от тривиального блокирования пользовательской активности.

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

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

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











Следует обратить внимание, что на странице с параметрами отчета нам дополнительно пришлось предусмотреть обработку ситуации, когда календарь находится в процессе загрузки. Для этого вводится обработчик события onblur, в задачи которого входит отслеживание потери фокуса в кнопках запуска календаря путем обнуления значения переменной состояния calendar_show_field.

В результате такого отслеживания состояния при загрузке календарь сможет отобразить себя в правильной позиции – рядом с тем полем даты, которое было в фокусе последним, а если пользователь в ходе загрузки календаря позиционировался на какое-то другое поле, отличное от даты, то календарь в момент инициализации не откроется. С учетом этого программный код, определяющий поведение календаря в момент загрузки в окно IFRAME, может принимать следующий вид:





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

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

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

$_SERVER["DOCUMENT_ROOT"]."/cgi-bin/footer.php"; ?>