Ордена ленина институт прикладной математики им. М. В. Келдыша Российской Академии Наук
Вид материала | Документы |
1.Режимы клиент-серверного взаимодействия и их особенности |
- Ордена Ленина институт прикладной математики им. М. В. Келдыша Российской Академии, 358.74kb.
- Г. в институте прикладной математики им келдыша в москве адрес тезисов: Оглавление, 1229.26kb.
- И. Б. Щенков из истории развития и применения компьютерной алгебры в институте прикладной, 1005.41kb.
- Задача дирихле для фрактального уравнения лапласа в прямоугольной области масаева, 20.93kb.
- М. Б. Гавриков, А. А. Таюрский Институт прикладной математики им. М. В. Келдыша ран,, 17.35kb.
- Г. И. Змиевская, А. Л. Бондарева, В. Д. Левченко, Т. В. Левченко Институт прикладной, 25.58kb.
- Г. Г. Малинецкий Институт прикладной математики им. М. В. Келдыша ран, 1009.67kb.
- Печатная или на правах рукописи, 249.64kb.
- Научно-исследовательский институт теории и истории изобразительных искусств ордена, 5597.47kb.
- Торгово-рекреационный комплекс «Охотный ряд» на Манежной площади; Атриум Старого Гостиного, 30.7kb.
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 как:
// аналогично
// event=”onclick”>
В данном случае объект calendar доступен нам на странице с параметрами отчета все время, и он инициализируется в обработчике события onload. Мы находим эту компоненту (idCalendarComponent) на странице и запоминаем ссылку на нее в переменной calendar. Соответственно, теперь нет никакой проблемы, чтобы открыть календарь в контексте нужного поля даты, непосредственно обратившись к его методу calendar.show
Однако, существует много ситуаций, когда мы не можем себе позволить включить в форму все компоненты, которые могут потребоваться для ее заполнения. Например, если поле заполняется с помощью иерархического классификатора большой мощности (например географического, с континентами, странами, городами), то такой объемный классификатор разумно получать частями. Кроме того, программный код, отвечающий за работу календаря может оказаться не таким маленьким, и мы вполне могли бы пойти на то, чтобы загружать его по требованию, с целью уменьшить время начальной загрузки страницы.
Если загружать календарь через отдельное обращение к серверу, то при реализации такого варианта возникает искушение ничего по большому счету не менять в программном коде, и использовать синхронный режим обмена между клиентом и сервером, который часто практикуется в ряде AJAX-приложений. Раньше это было не так-то просто сделать, потому что все API стандартного клиента исключали возможности синхронного обращения к серверу и, чтобы этого добиться, надо было пускаться на разные хитрости. Однако, сегодня стал доступен компонент XMLHttpRequest, который можно считать уже стандартным (см. [18], [19]) и в котором заложена возможность синхронного обращения (атрибут asynс, управляющий режимом обмена). В этом случае представленный выше программный код может быть переписан следующим образом:
Недостатком такого решения очевидно является то, что пока календарь не загрузится (т.е. пока не закончится выполнение функции load_calendar), вся работа формы замрет, она перестанет отвечать на события клавиатуры и мыши. В частности пользователь в этот момент не сможет ввести дату в поле вручную или, вспомнив, что дата находится в буфере обмена (clipboard), вставить ее оттуда, или, наконец, решить, что ему лучше сначала заполнить другое поле формы. Другими словами интерфейс сразу сильно теряет в своей дружелюбности, хотя и остается формально говоря «насыщенным».
Для того, чтобы избавить реализацию от подобных недостатков, компонента должна загружаться с сервера в асинхронном режиме. Вообще говоря, данный режим является основным, принятым в современных стандартных Веб-клиентах, способом обращения к серверу. Причем, программистам для работы в асинхронном режиме не требуется решать каких-то запредельно сложных задач, подобных обслуживанию сетевого взаимодействия на уровне сокетов TCP/IP. В равной мере, в тексте программ не надо выделять критические секции, поскольку модель исполнения программного кода, написанного на языке сценариев (" onclick="return false">
Однако сказанное не означает, что в среде Веб-клиента вообще не приходится сталкиваться с трудностями реализации в асинхронном режиме. Так, например, следует учитывать, что отправленный на сервер запрос не исполняется немедленно. В этой связи, приложение может воспользоваться поступающими с сервера результатами только спустя какой-то промежуток времени, и в этот период оно должно поддержать какое-то разумное поведение, отличное от тривиального блокирования пользовательской активности.
Особенности асинхронного режима в среде Веб отличают его от традиционных «настольных» клиент-серверных систем. В последних асинхронно выполняется только передача данных, в то время как все клиентское приложение (программный код, определения внешних форм и т.п.) находятся на компьютере пользователя, и их не надо подкачивать с сервера в процессе работы. С другой стороны, в процессе рабочего цикла Веб-приложения с сервера в асинхронном режиме передаются не только данные, но и программные компоненты. В результате Веб-приложение на клиенте приобретает специфическую, динамически изменяющуюся, полу-детерминированную программную структуру, трансформация которой ведется в условиях, когда мы точно знаем состояние структуры до и после трансформации, однако не можем точно предсказать, когда эта трансформация завершится.
Указанные особенности не вводят каких-то неразрешимых проблем при программировании клиентской составляющей Веб-приложения, хотя и делают реализацию значительно более сложной, требующей от разработчиков определенных навыков и концентрации. Сегодня на практике применяется простой и, наверное, универсальный подход для решения задач характерных для асинхронного клиент-серверного обмена, подразумевающий анализ всех возможных вариантов состояния, в которых могут находиться отдельные компоненты системы (не загружен, загружен, находится в процессе загрузки). Исходя из этого, можно предусмотреть отдельную реализацию для каждого из требующих самостоятельной обработки случаев (или группы случаев).
Продемонстрируем применение этого подхода на рассмотренном ранее примере с формой для задания параметров отчета, предполагая, что теперь календарь будет загружаться в отдельном окне, представленном в виде вложенного фрейма (html-элемент IFRAME). В этом случае необходимо различать состояние когда календарь загружен, и тогда его можно сразу связать с соответствующим полем. С другой стороны, если календарь не загружен, то необходимо определить переменные для представления состояния, в соответствии со значениями которых календарь после загрузки выполнит то, что ему предписано - откроется рядом с соответствующим полем даты. Реализация такого поведения может быть поддержана следующим программным кодом:
Следует обратить внимание, что на странице с параметрами отчета нам дополнительно пришлось предусмотреть обработку ситуации, когда календарь находится в процессе загрузки. Для этого вводится обработчик события onblur, в задачи которого входит отслеживание потери фокуса в кнопках запуска календаря путем обнуления значения переменной состояния calendar_show_field.
В результате такого отслеживания состояния при загрузке календарь сможет отобразить себя в правильной позиции – рядом с тем полем даты, которое было в фокусе последним, а если пользователь в ходе загрузки календаря позиционировался на какое-то другое поле, отличное от даты, то календарь в момент инициализации не откроется. С учетом этого программный код, определяющий поведение календаря в момент загрузки в окно IFRAME, может принимать следующий вид:
Представленный пример наглядно иллюстрирует недостатки распространенного сегодня подхода к реализации, который предполагает анализ набора всех возможных комбинаций состояния компонент системы и создание отдельного варианта реализации для каждого из случаев, требующих самостоятельной обработки. Видно, что это приводит к дублированию программного кода, когда одно и то же действие (в примере – открытие календаря) вынужденно реализуется с использованием разных механизмов и в разных местах (на странице с параметрами отчета и в коде календаря, загружаемого в IFRAME). Если нужная компонента (в данном случае – календарь) загружена, то для передачи управления в нее достаточно простых прямых вызовов. С другой стороны, когда компонента не загружена или находится в процессе загрузки, то приходится направлять взаимодействие в обратную сторону. Для этого оказывается необходимым вводить для представления состояния переменные, в соответствии с которым компонента на фазе своей инициализации выполнит нужные действия (т.е. действия, которые при прямом обращении производились непосредственными вызовами). Подобная техника является гораздо более трудоемкой по сравнению с синхронной подкачкой, но это на данный момент представляется неизбежной платой за обеспечение требований к построению удобного, насыщенного пользовательского Веб-интерфейса, по сравнению с которыми проблемы реализации уходят на второй план.
Отмеченные на этом простом примере сложности многократно увеличиваются, когда мы переходим к рассмотрению реальных Веб-систем. К числу таких, например, можно отнести биржевой торговый терминал (см. раздел 4), в котором требуется синхронизировать работу многих компонент - окна с котировками и графиками изменения цены финансовых инструментов; управление номенклатурой торгуемых инструментов, отображаемой на экране, и т.п. Качественное программирование подобных систем является действительно очень сложной задачей, когда приходится тщательно проектировать и реализовывать не только логику работы самого приложения, но и учитывать внешние факторы влияющее на его работоспособность, например, скорость соединения с сервером по каналам связи.
Решение проблемы взаимодействия между компонентами на стороне Веб-клиента в промышленных приложениях видится не в таком упрощении разработки, которое легко достигается за счет использования синхронной подкачки программного кода с сервера (что неизбежно сказывается на реактивности самого приложения), а за счет создания оригинальных механизмов, которые могли бы обеспечить реализацию взаимодействия компонент без ущерба для быстродействия приложения в асинхронном режиме. Один из таких механизмов, основанный на использовании процедур-триггеров для определения взаимодействия между программными компонентами, будет предложен в настоящей работе.