Параллельные вычисления в ИММ УрО РАН
 
 

Опыт построения вычислительных серверов приложений

В.В.Самофалов, А.В.Коновалов, Е.А.Старкова

Работа выполнялась при поддержке грантов РФФИ 01-07-90215, 00-15-96042.

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

Распределенный сервер приложений: зачем и как

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

Удивительно, но даже базовые механизмы управления параллельностью Явы, рожденной в начале 90-х, страдают от ограничений, ясно осознаваемых еще в конце 70-х: мониторы, как известно, сложны и существенно зависят от низкоуровневых сигналов [1]. Более высокоуровневые конструкции интересной в контексте распределенности среды JavaEE разрабатывались людьми совсем другого калибра, чем Д.Гослинг, и главная проблема этих конструкций проявляется еще более остро: их использование для построения архитектур, даже слегка отличных от неявно подразумеваемых создателями, затруднено до крайности. Суть проблемы здесь - в известном "отсутствуют лазейки" ("there is no escape") [4]. На этом фоне подход к инструментальному обеспечению распределенного программирования, наследующий функциональным эквивалентам Фортрана-IV, выглядит весьма соблазнительно. Вряд ли, однако, стоит так вот просто игнорировать многомиллиардные инвестиции в серверную (т.е., "распределенную") Яву.

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

  • Управление параллелизмом осуществляется явно, на основе высокоуровневых конструкций (в противовес ориентации Sun на автоматическое распараллеливание).
  • Удаленное взаимодействие происходит исключительно на основе асинхронных механизмов.
  • Ресурсы выделяются и освобождаются динамически, на всех этапах счета.

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

Cервер на основе EJB

Если присмотреться к требованиям, предъявляемым современной системе массового обслуживания, то выяснится (и это вполне естественно), что JavaEE все их покрывает. В таком случае наиболее близким аналогом "пользовательского процесса" будет серверный EJB-компонент (СК, т.е. entity или session bean). Дополнительный плюс такого подхода - возможность использовать при управлении заданиями хитроумные механизмы кеширования экземпляров на сервере приложений.

Существенная проблема тут - блокирующая природа коммуникаций в стандарте EJB 1.1. Понятно, что это ограничение - крайне жесткое, и появление в стандарте EJB 2.0 СК, управляемых сообщениями (message-driven bean) - крайне естественно. К сожалению, производительность опробованной свободной реализации базового стандарта JMS JBossMQ 0.8 оказалась крайне неудовлетворительной, особенно что касается привносимых задержек. Поэтому, был выбран иной путь - асинхронность взаимодействия обеспечивается нитями. Такое поведение СК запрещено стандартами (скорее всего, из соображений упрощения кластеризации), но в используемом нами свободном сервере приложений jBoss 2.x ни к каким неблагоприятным последствиям не приводит.

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

Асинхронные коммуникационные средства

Выявление узких мест в производительности довольно быстро выводит нас на уровень RMI - базового механизма удаленного взаимодействия в JavaEE. Измерения показывают, что современные версии виртуальных Ява-машин (IBM 1.3.0, Sun HotSpot 1.3.0) создают примерно впятеро большую задержку при использовании RMI по сравнению с TCP сокетами. Интересно, что в [3] приводятся данные о 20-кратном разрыве. Причины столь резкого улучшения прозрачны (переход от PII-233 в [3] к PIII-800 в нашем случае и совершенствование виртуальных Ява-машин), но, учитывая, что минимальная для вычислительных кластеров архитектура при использовании 100Mb EtherNet и так получается крайне несбалансированной, построение полноценной инфраструктуры для распределенных вычислений должно начинаться с базового уровня (т.е. с создания качественной замены RMI).

Предлагаемый подход, по сути, является культивирующим асинхронность синтезом активных сообщений [2] и RPC, и состоит в следующем. Удаленное взаимодействие начинается с регистрации отложенного вызова процедуры. Это локальная операция, она возвращает описатель запущенной операции. Естественно, что после регистрации одного вызова отправитель может сразу же регистрировать следующий. Как и во многих других системах, имея описатель, можно проверить, закончился ли вызов, либо подождать завершения. Удаленный узел, получив задание, запускает соответствующий код, который выполняет полезную работу и определяет, считать ли вызов удачным или не считать. Если вызов удачен, то, вообще говоря, получатель посылает пакет с результатом вызова. Если же вызов определяется как неудачный, то выполнение всех вызовов (находящихся на получателе, в пути, на сервере и даже еще не зарегистрированных) вплоть до терминатора, перехватывающего неудачные вызовы, отменяется. (Подобный механизм - прямой аналог исключений для асинхронного случая.)

Предварительные результаты показывают, что, даже в случае локального 100 Mb EtherNet, отложенный вызов процедур позволяет выполнить вчетверо больше вызовов/сек по сравнению с Java RMI. Такой результат, конечно же, совершенно понятен. Однако определяющий вопрос здесь - вовсе не производительность, а удобство и естественность метафоры взаимодействия [5].

Рассмотрим альтернативы предложенному выше подходу. Совмещение счета и обменов - основной способ борьбы с коммуникационными задержками для суперЭВМ [7], и тем более оно важно для большого Интернета и малобюджетных TCP-кластеров. Оставаясь в рамках RPC, совмещения счета и обменов можно добиться, запуская RPC из разных нитей (что неприемлемо, если действия логически связаны), либо вызывая сразу группу операций (что негибко). Третий подход описан в [3], где проблема решается использованием Java RMI для некритичных участков, и прямой работой со средствами нижнего уровня (TCP) в значимых для производительности местах. Еще один подход к снижению задержек в RPC состоит в кешировании удаленных объектов на локальном узле [6]. Этот подход может служить удачным дополнением к изложенному в настоящей работе.

xxx

Результаты, приводимые в работе, получены на МВС-1000/16 (счетный процессор PIII-800Mhz). Хотелось бы выразить благодарность С.В.Шарфу (ИММ УрО РАН) за неоднократные полезные дискуссии, а С.А.Ханину (УрГУ) - за участие в проведении измерений.

Литература:

  1. Н.Джехани Язык Ада. М.: Мир, 1988.
  2. T.vonEicken, D.Culler, S.C.Goldstein, K.E.Schauser Active messages: a mechanism for integrated communication and computation. // Proc. of the 19th International Symposium on Computer Architecture, pages 256--266, May 1992.
  3. K.A.Hawick, H.A.James, J.A.Mathew, P.D.Coddington Java Tools and Technologies for Cluster Computing. Department of Computer Science, University of Adelaide, Technical Note DHPC-077, 1999.
  4. B.Kernighan Why Pascal is Not My Favorite Programming Language. Bell Labs Computing Science Technical Report, Vol. 100, April 1981.
  5. A.Konovalov, V.Samofalov, S.Scharf Virtual Shared Files: Towards User-Friendly Inter-Process Communications // Parallel computing technologies: 5th international conference; PaCT-99, St. Petersburg, Russia, Sept. 6-10, 1999, Victor Malyshkin (ed.). LNCS 1662. (1999) 223-228.
  6. J.Maasen, R.van Nieuwpoort, R.Veldema, H.E.Bal, A.Plaat An Efficient Implementation of Java's Remote Method Invocation. // Proc. ACM Symposium on Principles and Practice of Parallel Programming. May 1999.
  7. A.V.Zabrodin, V.K.Levin, V.V.Korneev The Massively Parallel Computer System MBC-100. // Parallel Computing Technologies, Proc., 1995. LNCS 964. (1995) 341-355.