Пересекая границы: Continuation, Web-разработка и Java-программирование

Модель с запоминанием для программистов, работа без запоминания для пользователей

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

Брюс Тэйт, президент, RapidRed

Брюс Тэйт (Bruce Tate) является отцом, горным байкером и байдарочником, проживающим в Austin, Texas. Он автор трех бестселлеров по языку Java, в том числе, победителя Jolt "Лучше, быстрее, легче Java". Недавно издал "За пределами Java"… Он работал 13 лет в IBM, а сейчас является основателем RapidRed consultancy, где специализируется на стратегиях и архитектурах облегченной разработки, основанных на технологии Java и Ruby on Rails.



13.06.2007

О данной серии статей

В серии статей "Пересекая границы" ее автор Брюс Тэйт развивает мнение о том, что современные Java-программисты имеют широкие возможности для изучения других подходов и языков. Сфера программирования изменилась после того, как Java-технология стала очевидным лучшим выбором для всех разрабатываемых проектов. Другие интегрированные среды формируют способ построения Java-сред, а концепции, осваиваемые вами в других языках, могут дать новый импульс вашему Java-программированию. Написанный вами Python-код (или Ruby, или Smalltalk, или ...) может изменить способ вашего Java-кодирования.

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

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

Появление Web

Когда в середине 1990-х годов вся индустрия переходила на Web-разработку, разработчики программного обеспечения были в восторге. Клиент-серверные приложения, которые мы создавали, были более дружественны к пользователям по сравнению с альтернативами терминал-хост, но нас мучило несколько проблем:

  • Производительность часто была ужасной. Одним из преимуществ терминальной разработки является ограничение программной моделью расходов на взаимодействие. После устранения этих ограничений нам стало не хватать пропускной способности, инструментальных средств или квалификации для создания простых распределенных приложений.
  • Приложения были не переносимы. Большинство сред разработки клиент-серверных приложений навязывали нам аппаратное и программное обеспечение.
  • Приложения были тяжелы в развертывании. Потенциально мы должны были управлять тысячами клиентов независимо.
  • Наибольшие затраты были скрыты. Развертывание превращалось в самое серьезное ограничение, поскольку стоимость завершающего этапа внедрения взмывала вверх.

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

Появление Web-разработки

Взрывообразное распространение Web-разработки произошло в середине 1990-х годов. С появлением языка программирования Java стало можно создавать распределенные Web-приложения и одновременно решать наиболее серьезные клиент-серверные проблемы при помощи новых возможностей:

  • Ограниченные взаимодействия. Web-модель запрос/ответ обладает всеми характеристиками терминальной разработки. Пользователь вводит информацию в форму, выполняет запрос и получает ответ. "Болтливость" при взаимодействиях исчезла, а производительность повысилась.
  • Архитектуры, не использующие общие ресурсы. Основанная на сервлетах модель программирования могла не сохранять состояния. Это означало, что один сервлет мог обслуживать любого клиента, а фиксированный пул сервлетов мог обслуживать намного большее число клиентов. Вы не должны были резервировать сервлет для каждого пользователя. Производительность улучшилась еще больше.
  • Общие стандарты для клиентов. Неожиданно, развернув общий браузер у всех клиентов, вы могли создать один интерфейс и взаимодействовать практически со всеми клиентами. Поддержка многих браузеров была проблематичной, но это и близко не было так трудно, как поддержка родных библиотек пользовательских интерфейсов. Многие проблемы переносимости просто исчезли.
  • Более совершенная модель развертывания. Используя браузер в качестве стандартного клиентского приложения, стало намного проще распространять разработки. Компания могла развернуть приложение на паре Интернет-серверов и предоставить доступ к ним для всего предприятия. Сетевая архитектура часто могла распределять запросы по нескольким серверам, поэтому увеличить мощность стало так же просто, как установить еще один сервер. Развертывание на стороне клиента было настолько простым, что сводилось к проверке установки у клиента нужного браузера. Обслуживание значительно упростилось.

Производительность, масштабируемость и переносимость, все стало намного лучше, и Интернет-революция включила последнюю передачу. Но мы должны были пойти на некоторые значительные компромиссы.

Не утопия

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

Рисунок 1. Web-приложения выполняют пары запрос/ответ
Рисунок 1. Web-приложения выполняют пары запрос/ответ

В этой модели браузер находится под жестким управлением. Приложение может реагировать только на запросы, сделанные браузером. Эта модель работает тогда, когда запросы являются маленькими и независимыми, но это существенное ограничение для Web-приложений, управляющих многокомпонентными приложениями. Самым распространенным примером являются интерактивные покупки. Нажмите на кнопку Submit два раза, и вы часто непреднамеренно закажете два одинаковых товара. Покинув Web-магазин и вернувшись через две недели, вы можете обнаружить эти же товары в вашей корзине, вовсе не ожидая этого.

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

  • Получить идентификатор пользователя от клиента
  • Восстановить состояние взаимодействия из сессии
  • Обработать пользовательский запрос
  • Создать ответ
  • Сохранить состояние взаимодействия в сессии
  • Послать ответ пользователю

Я очень упростил проблему, поскольку использование не запоминающей состояния модели для эмулирования запоминающих состояние приложений может вызвать еще более серьезные проблемы. Самый важный вопрос также стар, как и Web-разработка: как обработать кнопку "Назад" (Back)?


Новые ответы на старые вопросы

Для вас может быть шоком узнать, что можно создать Web-приложение, работающее без запоминания состояния для пользователя, но использующее модель с запоминанием состояния для программиста. На самом деле еще в 1995 году в книге "Хакеры и художники" (см. раздел "Ресурсы") Пол Грэхэм (Paul Graham) говорил о применении такого подхода в ViaWeb. Этот подход использовал программную управляющую структуру, называемую continuation (преемственность).

Основная идея заключается в следующем: вы можете позволить вашей интегрированной среде программирования загрузить состояние вашего приложения перед запросом и сохранить состояние вашего приложения после каждого запроса. Я начну с рассмотрения continuation в языке программирования Ruby.

Ruby-пример

Если вы хотите кодировать самостоятельно, установите Ruby и выполните команду irb. Определите метод под названием loop, введя команды после символа >, как показано в листинге 1:

Листинг 1. Создание метода loop
irb(main):001:0> def loop(interrupt)
irb(main):002:1> for i in 1..10
irb(main):003:2> puts "Value of i: #{i}"
irb(main):004:2> callcc {|c| return c} if i == interrupt
irb(main):005:2> end
irb(main):006:1> end
=> nil

Метод loop принимает один параметр interrupt. Вы начинаете цикл for с 1 до i, распечатываете значение i, а потом ... останавливаетесь. Загадочный оператор callcc означает вызов с использованием continuation. Рассматривайте continuation как состояние программы в замороженной точке времени. Ruby вызывает блок кода в фигурных скобках, передавая объект continuation. Код в фигурных скобках является замкнутым выражением и просто блоком кода, передаваемым в callcc. Конечным результатом является сбор информации о состоянии процедурой callcc и сохранение ее в переменной c. Вы можете теперь вызвать этот метод и прервать выполнение в любой точке цикла, сохраняя состояние программы. Затем вы в любое время можете продолжить.

Теперь выполните метод несколько раз, как показано в листинге 2:

Листинг 2. Выполнение метода loop
irb(main):007:0> cont = loop 5
Value of i: 1
Value of i: 2
Value of i: 3
Value of i: 4
Value of i: 5
=> #<Continuation:0x2b5a358>
irb(main):008:0> cont.call
Value of i: 6
Value of i: 7
Value of i: 8
Value of i: 9
Value of i: 10
=> 1  10
irb(main):009:0> cont = loop 8
Value of i: 1
Value of i: 2
Value of i: 3
Value of i: 4
Value of i: 5
Value of i: 6
Value of i: 7
Value of i: 8
=> #<Continuation:0x2b562f0>
irb(main):010:0> cont.call
Value of i: 9
Value of i: 10

При каждом вызове continuation запоминает, где прервалось выполнение. То есть, среда Web-разработки, использующая continuation, может захватить continuation после обработки каждого запроса и записать в сессионную переменную при помощи идентификатора. Затем среда может восстановить continuation из сессии перед каждым новым запросом, используя тот же подход, который вы использовали для хранения пользовательских данных.

За и против

Подход с использованием continuation-сервера во многих случаях позволяет вам иметь ваш кусок пирога, а также есть его - модель программирования с запоминанием состояния и работа пользователя при производительности систем без запоминания состояния. Углубляясь далее, перечислим преимущества такого подхода:

  • Он гарантирует запоминание состояние между запросами. Интегрированная среда может идентифицировать конкретные continuation в URL и сохранять их в сессии.
  • Он предоставляет программную модель с запоминанием состояния. Интегрированная среда может восстановить любую continuation в любое время. Если пользователь подтверждает форму второй раз, continuation может подхватить обработку на более раннем временном отрезке.
  • Вы можете отменить continuation на основе бизнес-правил, поэтому легко защитить форму от двойного подтверждения.
  • Вы бесплатно получаете поддержку кнопки "Назад". Поскольку у вас без преувеличения имеется состояние выполнения в любой момент времени, интегрированная среда может просто восстановить соответствующую continuation, если пользователь когда-либо нажмет кнопку "Назад".
  • Организовать несколько потоков теперь легко, поскольку нет совместно используемых ресурсов. Отсутствие соревнования за ресурсы означает отсутствие большинства проблем многопоточности.

Continuation значительно упрощают модель Web-разработки. Используя continuation, вы можете рассматривать Web-приложение как приложение с последовательностью запросов для предоставления дополнительной информации пользователя. На рисунке 2 показана пересмотренная схема работы приложения. После начала работы пользователя с приложением Web-сервер вступает в действие. Вместо обслуживания произвольных запросов в произвольном порядке, приложение становится цельным, направленным на взаимодействие с пользователем.

Рисунок 2. Continuation позволяют приложению функционировать более естественным способом
Рисунок 2. Continuation позволяют приложению функционировать более естественным способом

Аналогично многим высокоуровневым абстракциям, continuation имеют и отрицательные стороны. Эти недостатки будут переноситься на весь подход, зависящий от них. Continuation-серверы должны решать следующие общие проблемы:

  • Continuation могут быть дорогостоящими. Облегчение помещения данных в сессию означает то, что больше пользователей будет помещать больше данных в сессию. Проектировщики интегрированной среды могут уменьшить затраты, используя подход, называемый "частичные continuation", при котором интегрированная среда сохраняет только относящуюся к приложению часть continuation.
  • Ужасные URL. Большинство continuation-серверов добавляют странные идентификаторы для continuation.
  • Изменение парадигмы работы с пользователем. Если вы используете continuation-серверы повсюду, работа пользователя изменится. Кнопка "Назад" действительно будет работать, и это может дезориентировать некоторых пользователей. Например, нажав кнопку "Назад" после помещение какого-нибудь товара в вашу корзину, ожидали бы вы, что приложение уберет его из корзины?

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

Реализации в других языках

Оказывается, что несколько языков программирования используют основанные на continuation подходы (ссылки на дополнительную информацию обо всех них приведены в разделе "Ресурсы"). Наиболее распространенные есть в Lisp, Ruby и, более всего, в Smalltalk.

  • Seaside, самый распространенный continuation-сервер, реализован на Smalltalk Эви Бриантом (Avi Bryant). Если вы хотите познакомиться с радикально отличающейся и очень производительной интегрированной средой Web-разработки, выберите Seaside. Работа с ней изменит ваш подход к Java-программированию.
  • Интегрированная среда Iowa на Ruby была тоже создана Эви Бриантом под воздействием Web Objects. Сейчас она используется и поддерживается в Ruby.
  • Интегрированная среда Wee - это еще одна Ruby-среда, имеющая много характеристик continuation-сервера, но не использующая continuation на родном языке.
  • ViaWeb использовала continuation с языком Lisp для получения лучшей производительности по сравнению с конкурентами в 1995.
  • Continuity - это написанный на Perl continuation-сервер. Perl 6 будет иметь встроенную поддержку continuation.

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


Continuation-серверы на языке программирования Java

Java-разработчики со все возрастающим интересом наблюдают за возможностями основанного на использовании continuation подхода к созданию интегрированных Web-сред, но язык Java не имеет встроенной поддержки continuation. Если ваш язык не поддерживает continuation, у вас есть ограниченное число вариантов: эмулировать continuation, добавить continuation к вашему языку или выбрать новый язык. Различные интегрированные Java-среды (см. раздел "Ресурсы") используют каждый из этих подходов:

  • Эмулирование continuation. Некоторые интегрированные Java-среды используют альтернативные методы для сбора состояния выполнения программы. Lakeshore использует потоки, а Spring Web Flow использует конечные автоматы (более подробно вы узнаете о Web Flow в данной статье позднее).
  • Выбор другого языка. Виртуальная машина Java имеет альтернативные языки, которые могут работать поверх нее. Cocoon 2 использует такой подход при помощи Rhino, виртуальной машины, поддерживающей JavaScript, но также и Java.
  • Реализация continuation. Этот подход используют контейнер сервлетов Jetty 6, RIFE и WebWork.

Эмулирование встроенных continuation

Вы не должны использовать continuation для захвата состояния выполнения программы. Приостановленные потоки и конечные автоматы могут прекрасно захватывать состояние выполнения. Подход с использованием потоков просто "замораживает" и сохраняет существующий поток. Этот подход немного более ограничен, поскольку не обеспечивает производительность не запоминающих состояния программ - фактически вы используете отдельный поток для каждого пользователя.

Но конечный автомат является прекрасным подходом для эмулирования continuation. Конечный автомат - это программа с четко определенными переходами между состояниями. Самой популярной Java-средой с большим влиянием continuation-сервера является Spring Web Flow. Web Flow моделирует навигацию по страницам пользовательского интерфейса в виде конечного автомата. Web Flow может моделировать процессы в виде Java-объектов, но обычно для описания процесса используется XML. Рассмотрите представление процесса в листинге 3 из руководства по Web Flow (см. раздел "Ресурсы"):

Листинг 3. Представление процесса в Web Flow
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE webflow PUBLIC "-//SPRING//DTD WEBFLOW//EN"
        "http://www.springframework.org/dtd/spring-webflow.dtd">

<webflow id="myFlow" start-state="displayForm">

    <view-state id="displayForm" view="form">
        <entry>
            <action bean="myFlowAction" method="setupForm"/>
        </entry>
        <transition on="submit" to="processSubmit">
            <action bean="myFlowAction" method="bindAndValidate"/>
        </transition>
    </view-state>

    <action-state id="processSubmit">
        <action bean="myFlowAction"/>
        <transition on="success" to="finish"/>
    </action-state>
        
    <end-state id="finish" view="success"/>

</webflow>

Процесс имеет три основных состояния: displayForm, processSubmit и finish. Процесс определяет действия, которые будут вызывать переход автомата из одного состояния в следующее. Например, подтверждение будет вызывать переход состояния из displayForm в processSubmit. Web Flow работает над обычным уровнем модель-представление-контроллер. Вы можете использовать много разных технологий просмотра для отображения ваших форм.

При переходе из одного состояния в следующее интегрированная среда автоматически запоминает текущее состояние со всеми переменными экземпляра. Вы получаете такую же поддержку кнопки "Назад" и программную модель с запоминанием состояния, которую обеспечивают и другие continuation-серверы. Этот подход не использует встроенные continuation, но вы получаете многие преимущества continuation-серверов, а также другие преимущества:

  • Персистенция. Хотя нельзя захватить весь стек вызовов, но вы можете захватить текущее состояние выполнения и даже сохранить текущее состояние.
  • Долгоживущие процессы, такие как потоки работ (workflow). Поскольку состояние не зависит от данного стека вызовов, подход более стабилен для таких элементов, как длительные потоки работ.
  • Инструментальные средства. Относительно легко создать инструментальные средства для XML.

Альтернативные языки

Cocoon использует Rhino, виртуальную машину JavaScript, которая подключается в JVM. Cocoon-контроллеры используют модифицированную версию Rhino для выражения continuation, поэтому Cocoon позволяет вам выражать логику вашего контроллера на Java или JavaScript. Взгляните на приложение, приведенное в листинге 4 и взятое из руководства по Cocoon (см. раздел "Ресурсы"):

Листинг 4. Использование Cocoon
  try {
    if (operator == "plus")
      cocoon.sendPage("result.html", {result: a + b});
    else if (operator == "minus")
      cocoon.sendPage("result.html", {result: a - b});
    else if (operator == "multiply")
      cocoon.sendPage("result.html", {result: a * b});
    else if (operator == "divide")
      cocoon.sendPage("result.html", {result: a / b});
    else
      cocoon.sendPage("invalidOperator.html", {operator: operator});
  }
  catch (e) {
    cocoon.sendPage("error.html", 
      {message: "Operation failed: " + e.toString()});
  }

Обратите внимание на метод sendPage в листинге 4. Этот метод посылает страницу назад пользователю. Вы не увидите какого-либо обычного громоздкого кода для поддержки кнопки "Назад" или для сохранения пользовательских данных в сессии – все это инкапсулировано в среду Cocoon.

Реализация continuation

Интегрированная среда RIFE реализует свои собственные continuation, а интегрированная среда WebWork использует реализацию continuation в RIFE. Jetty 6 также содержит реализацию continuation на Java. В листинге 6 показан пример из руководства по RIFE для игры в угадывание числа:

Листинг 5. RIFE-пример
while (mGuess != answer) {
  print(template);
  pause();
  guesses++;

  if (answer < mGuess) {
    template.setBlock("indication", "lower");
  } else if (answer > mGuess).{
    template.setBlock("indication", "higher");
  }
}

В этом примере метод pause() захватывает continuation и посылает шаблон (template) назад пользователю для действия. RIFE делает continuation простыми и доступными для Web-разработчика.


Ближайшее будущее

Вы можете заметить, что continuation представляют реальное совершенствование интегрированных сред Web-разработки. При использовании этого подхода вы можете повысить производительность. А поскольку Web-приложение выражается в виде понятного Java-кода (вместо сотен разъединенных сервлетов), ваши приложения намного легче понять и поддерживать.

Новые достижения в Web-разработке быстро делают подход с continuation все более важным. Вместо выборки полных Web-страниц в традиционных моделях запрос/ответ, Ajax-приложения могут асинхронно выбирать маленькие фрагменты Web-страницы и вставлять результаты в существующую страницу. Но Ajax-приложения вынуждают поддерживать соединение с пользователем длительный период времени, для того чтобы сохранить "отзывчивость" приложения и легкость кодирования системы отслеживания состояния. Такой режим работы аннулирует назначение программирования без запоминания состояния, поскольку вы действительно должны удерживать ресурсы для каждого подключенного пользователя. Используя continuation, вы можете сохранять состояние в них и восстанавливать состояние при необходимости.

В ближайшем будущем развитие аппаратного обеспечения сделает дополнительные затраты ресурсов на continuation менее существенными. Без полного пересмотра интегрированные среды Web-разработки все равно будут слишком сложными. Ajax предвещает еще большее усложнение Web-разработки. Все эти факторы неуклонно приведут к использованию continuation-сервера. Через два года большинство новых Web-разработок будет использовать какой-либо continuation-сервер или какую-либо эмуляцию continuation.

В следующий раз я расскажу о предметно-ориентированных языках и их роли в Ruby on Rails. Затем я рассмотрю суть идеи и покажу вам, что (в этом контексте) происходит в Java-программировании.

Ресурсы

Научиться

Получить продукты и технологии

  • RIFE: RIFE - это инновационная интегрированная среда, которая использует continuation и многие другие методики, популярные в динамических языках.
  • Spring's Web Flow: Spring Web Flow - это общий механизм процессов, предназначенный для определения и выполнения процесса предоставления страниц в Web-приложении .
  • Lakeshore: Lakeshore - это основанная на Java интегрированная компонентная Web-среда, на которую оказали решающее влияние Seaside 2 и среда Borges Ruby.
  • Jetty 6: Jetty - это контейнер сервлетов на языке Java с поддержкой continuation.
  • Seaside: Seaside - это самый популярный и влиятельный continuation-сервер.
  • Wee: Wee - это еще одна интегрированная среда Ruby continuations.
  • Continuity: Continuity - основанная на continuation интегрированная среда Web-программирования для Perl.

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java, Open source
ArticleID=230467
ArticleTitle=Пересекая границы: Continuation, Web-разработка и Java-программирование
publish-date=06132007