Зачем нужны сервера приложений, если есть TomCat

:

Молодые программисты часто задают вопрос: а зачем нужны зачастую довольно тяжелые и дорогие промышленные сервера приложений (такие как JBoss AS, Oracle WebLogic, IBM WebSphere AS), если у нас есть замечательный легковесный фреймворк Spring и контейнер сервлетов Apache TomCat. Попробуем на него ответить. Сразу замечу, речь сейчас не идет об архитектуре приложения! Не важно, используете вы EJB или нет. Предположим, у вас приложение на Spring Framework и стоит вопрос, на чем его запускать. Итак, какие дополнительные сервисы предлагает нам сервер приложений.

  • Пулы соединений с БД. Да, у TomCat тоже есть пул соединений, но каковы его возможности? Может ли он периодически тестировать доступность СУБД и обновлять соединения в случае восстановления после сбоев? Умеет ли он делать замену прав доступа? Грубо говоря, подключаемся к БД под пользователем в зависимости от того, кто аутентифицировался в нашем приложении, если часть логики вынесена на уровень СУБД, то это бывает полезно. Может ли пул соединений Tomcat балансировать нагрузку между несколькими базами данных (например, в случае Oracle RAC), а так же определять, что вот эти узлы RAC умерли и теперь к ним не нужно пытаться подключаться, а затем понять, что они снова доступны и теперь их тоже можно использовать? В конце концов, может ли ваш пул соединений защитить от некорректного кода в приложении, которое по недосмотру не возвращает соединения, просто отбирая его после какого-то таймаута?
  • JMS. Если вы хотите использовать очереди в вашем приложении, развернутом на TomCat, то придется отдельно еще поднимать сервера очередей сообщений. В случае сервера приложений, очереди как правило доступны их коробки. Вместе с очередями доступны так же следующие вещи: кластеризация - вы можете строить распределенные очереди, расположенные сразу на нескольких серверах, что существенно увеличивает масштабируемость и доступность вашего приложения, миграция - в случае падения одного из серверов, его очереди автоматически перемещаются на другой, сохраняя необработанные сообщения. В некоторых серверах приложений поддерживается Unit-of-Order - гарантированный порядок обработки сообщений, удовлетворяющих некоторым критериям, очень часто при интеграции бывает полезен.
  • JTA. Те самые распределенные транзакции. Кто-то их понимает и использует, кто-то считает слишком тяжелыми. Как правило это так, они слишком тяжелые, но если вам нужно обеспечить согласованность данных в СУБД, разнесенных по разным углам нашей необъятной, или в СУБД и очереди, то без таких транзакций будет трудно. Суть распределенных транзакций в том, что мы не коммитим ни в одну из БД, пока не убедимся, что все БД, участвующие в транзакции, смогут принять наши данные. Тем самым мы избегаем проблемы “с одного счета в одном банке деньги списали, а на другой - в другом банке - не зачислили - сработало ограничение целостности”.
  • Безопасность. Современные сервера приложений предоставляют множество различных провайдеров безопасности. Доступны различные провайдеры аутентификации, вы можете хранить ваших пользователей во множестве мест: во встроенном LDAP-сервере, в базе данных, во внешнем LDAP-сервере, в различных Internet-directory - специализированных приложениях для управления правами доступа. Возможны следующие сценарии: на работу наняли человека, добавили его в Internet-directory/Access-manager, там запустился процесс раздачи прав, который выдал человеку права на все ресурсы вашего предприятия и теперь каждый сервер приложений в вашей компании (а их может быть очень много) видит эти права, так как подключен к этой Internet-directory/Access-manager. Доступно разделение пользовательской сессии между приложениями: мы аутентифицировались в одном приложении - нам уже не нужно аутентифицироваться в другом. Так же доступна реализация Single-Sign-On: вы делаете один из серверов базой для хранения пользователей, все другие сервера при аутентификации пользователя обращаются к этой базе. Реализуется SSO посредством Security Assertion Markup Language (SAML) 1/2 или посредством Simple and Protected Negotiate (SPNEGO) и Kerberos для Windows-клиентов. Возможна авторизация посредством протокола eXtensible Access Control Markup Language (XACML), позволяющего описывать довольно сложные политики (например, приложение доступно пользователю только в рабочее время). Опять же все данные возможности работают в кластерном окружении. Впрочем, стоит отметить, что с помощью Spring Security и Apache Shiro можно реализовать большинство из них, но вам придется “тянуть” эти реализации за каждой вашей программой, в то время как в сервере приложений они доступны из коробки.
  • Масштабируемость и высокая доступность. Да, для TomCat мы можем настроить кластеризацию, но она будет довольно примитивной. Мы не сможем сделать передачу пользовательской сессии из одного центра обработки данных (ЦоД) в другой через Интернет, мы не сможем эффективно настроить репликацию сессий на большом кластере, состоящем из 40-50 экземпляров сервера приложений. В случае сбоев, мы не сможем обеспечить миграцию экземпляров сервера на другую машину и т.д. Так же в TomCat нет механизмов автоматического мониторинга и реакции на ошибки: мы не можем автоматически перезапустить экземпляр сервера, если на нем зависло 10 потоков, мы не можем автоматически отправить письмо администратору при переполнении пула соединений и т.д.
  • Управляемость. В случае большого кластера TomCat у нас нет единого центра управления, т.н. AdminServer и аналога NodeManager’а. Мы не сможем одновременно запустить на старт 50 экземпляров сервера. Мы не можем посмотреть состояние экземпляров, посмотреть сколько у нас обработчиков на той или иной очереди, на том или ином сервере, сколько создано соединений с той или иной БД, какие из них можно убить, какие в данный момент выполняются транзакции, какие ресурсы в них задействованы и т.д. Конечно, можно все сделать “за три минуты на скриптах, ну как в Линуксе принято”, но результат будет плачевный.
  • Скриптовый язык. Кстати о скриптах, большинство промышленных серверов приложений содержат утилиты для выполнения скриптов как правило на языке Python, Пользоваться данными утилитами одно удовольствие. Администратор может описать в виде скрипта все шаги для подготовке к развертыванию сколь угодно большого приложения, таким образом запуск в продуктив или обновление займет сравнительно немного времени. С помощью таких скриптов можно создавать источники данных (представьте себе сервисную шину, подключенную к 120 экземплярам БД), JMS-очереди, менеджеры потоков, создавать новые экземпляры серверов и добавлять их в кластер, выполнять остановку и запуск серверов, их миграцию.
  • Административный канал и развертывание в промышленном режиме. Некоторые сервера приложений позволяют включить так называемый административный канал - отдельный порт, защищенный SSL, запросы по которому имеют приоритет. Таким образом, даже если ваш сервер завис, вы сможете на него зайти и посмотреть, какие транзакции выполняются и какие потоки висят. Но у данного канала есть и другое применение. При обновлении приложения вам не нужно выключать старую версию! Вы можете добавить на сервер новую версию приложения в административном режиме - пользователи продолжают работать со старой, а по административному каналу доступна новая, соответственно мы можем выполнить последнее тестирование перед запуском, проверить все ли у нас правильно развернулось. Затем мы окончательно публикуем приложение, при этом пользователи, уже имеющие сессию, продолжают работать со старой версией, чтобы не потерять данные. Новые пользователи аутентифицируются на новой версии. Тем самым мы обновляем приложение без его простоя, что очень важно для критических систем.

Как мы видим, сервисов, предоставляемых промышленными серверами приложений, довольно много. Возникает вполне закономерный вопрос, а почему сервлет-контейнер TomCat такой популярный? Здесь есть несколько соображений:

  • В первую очередь, цена. За все хорошее нужно платить, за отличное платить еще больше, особенно, если мы хотим доступ к технической поддержке и патчам. К примеру, сервер приложений Oracle WebLogic в базовой комплектации стоит $10 000/processor (под processor здесь понимается одно ядро, умноженное на т.н. core factor). Не каждый заказчик может себе позволить такое решение.
  • Не всем приложениям нужны вышеперечисленные сервисы, а иногда разработчики не умеют ими пользоваться. Например, если у нас простая учетная система, работающая с одной БД, то нам не нужны распределенные транзакции. С другой стороны, масштабирование. Приложение может следовать всем Java EE спецификациям, но при этом не быть масштабируемым. Простой пример: приложение читает из БД измененные записи (которые пишутся с помощью триггеров в отдельную табличку) и передает их в другую БД. При этом авторы как-то забыли про блокировки. Если мы запустим данную программу на кластере, то у нас каждая запись будет обрабатываться N-раз, по числу экземпляров TomCat в кластере. Такая масштабируемость нам не нужна. Аналогичные соображения можно привести и для других сервисов.
  • Простота и легкость освоения. Вообще администратор сервера приложений это отдельная профессия, такая же как и администратор баз данных. Это не просто линукс-админ. Посмотрим еще раз на список сервисов и задумаемся как долго нужно изучать возможности выбранного сервера приложений по их реализации и настройке. Курсы по администрированию IBM WebSphere или Oracle WebLogic могут стоить десятки тысяч рублей.
  • Сварим кашу из топора сами. Бывают ситуации, когда это необходимо. Не всегда есть смысл ждать патча, исправляющего какие-то критические для нашего приложения ошибки. Гораздо быстрее просто подменить версию библиотеки. Правда зачастую это можно сделать и на сервере приложений, добавив библиотеку к нашему приложению и настроив загрузчик классов. Причем современные сервера содержат в себе утилиты для поиска ошибок в иерархии загрузчиков.

Отдельно отметим причины популярности Spring Framework как на TomCat’е, так и на промышленных серверах приложений и немного их прокомментируем:

  • Исторические причины. Почему Spring Framework, а не EJB? Ну потому что я в 88-м году программировал на С++, фигня этот ваш С++. Да, действительно EJB 1.1 и EJB 2.x были очень тяжелы для освоения и для использования, но времена меняются. Опять же, начиная с Java EE 6, появился легковесный IoC-контейнер - CDI. Зачем тянуть в свое приложение сотни мегабайт библиотек, которые будут существенно замедлять процесс развертывания, если можно использовать готовые и довольно качественные реализации, предоставленные производителем сервера приложений? На самом деле иногда есть зачем.
  • Баланс между завязкой на конкретном производителе и переносимостью. Да, EJB это часть спецификации Java EE, причем наиболее сложная, сложнее только J2CA и по хорошему приложения, написанные для одного сервера, должны работать на другом. На практике это не всегда так. Зачастую для эффективного использования всех возможностей сервера приложений приходится в коде вызывать его API, а это уже делает приложение непереносимым. Правда, справедливости ради, с каждой новой версией Java EE таких завязок становится все меньше. С другой стороны, даже без явных завязок на API части стандарта могут быть реализованы разными серверами по своему, например, один сервер будет закрывать EntityManagerFactory при остановке приложения, другой - нет. Реализации иерархии загрузчиков классов так же могут отличаться.
  • При этом, явная завязка на Spring Framework тоже имеет свои минусы. Это такая же завязка на производителе, как и решение использовать только WebLogic. Но если с WebLogic хоть и со скрипом мы сможем уйти, то со Spring Framework скорее всего нет. Что будет, если завтра ведущие разработчики решат оставить свое детище и все дружно перейти в Oracle? Впрочем, думаю, что вероятность такого сценария не высока.
  • Отдельно стоит отметить поддержку Spring Framework со стороны разработчиков серверов приложений. Например, в Oracle WebLogic можно включить отдельную страницу в консоли администрирования для каждого построенного на данном фреймворке приложения. На странице будет отображено дерево бинов и показаны их свойства. Так же доступны бины самого сервера и упрощена разработка MBean’ов. Помимо этого, Spring Framework прозрачно интегрируется в кластерное окружение, а Spring Security может использовать подсистему безопасности сервера приложений.

В заключение хочется отметить, что выбор платформы для приложения это довольно нетривиальная инженерная задача, в которой должна учитываться масса факторов. Это и соотношение стоимости разработки к стоимости поддержки (при этом нужно учитывать, что разработка может идти год, а использоваться ПО может десяток лет), стоимость самих серверов приложений, ваши отношения с вендором, т.к. несмотря на высокую номинальную стоимость зачастую предоставляются скидки под 80%. Учитывайте вашу и вашей команды квалификацию в конце концов. Ну и не нужно быть ретроградом, если вы в 2001-м писали на EJB и с тех пор смотреть на них не можете, то это еще не повод отказываться от этой замечательной технологии и реализующих ее серверов приложений, но даже если вы гуру Spring Framework, подумайте, может быть для него на промышленном сервере тоже найдется место?