Немало организаций ежедневно сталкиваются с необходимостью поддерживать работу высоконагруженных систем, от адекватного функционирования которых зависит огромное число операций. В таких условиях сложно представить ситуацию, когда работа системы приостанавливается для обновления компонентов сервера или кода программных приложений. И если приостановка загрузки картинки или воспроизведения видео на Facebook может быть некритичной, то в финансовой, военной или же аэрокосмической отрасли последствия даже минимальной задержки в обработке операций могут быть катастрофическими.

Чтобы лучше понимать масштаб данных операций, просто представьте, что банк не смог осуществить многомиллионный платеж клиента или что какая-либо из диспетчерских систем аэропорта «Хитроу» решила обновиться во время взлета самолета. Едва ли подобный сценарий допустим в сегодняшних реалиях.

Тем не менее, сервера приложений неизбежно приходится обновлять – никуда от этого не деться. А с учетом постоянной нагрузки на них вопрос бесперебойности работы приложений во время обновлений становится как никогда актуальным.

Что необходимо для обеспечения бесперебойной работы приложений во время обновления?


Существует несколько способов решения данной проблемы:

  1. Создание избыточного количества серверов приложений или окружений (кластеров).
  2. Особые возможности сервера приложений или непосредственно приложений.

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

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

Кому может быть интересно такое решение?


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

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

Особенности разработки сложных приложений


Одним из принципов разработки сложных приложений является построение модульных приложений, модули в которых слабо связаны. В мире Java подобную технологию хотели включить еще в JDK 7, позже решили выпустить ее вместе с Java 8, но теперь, судя по текущим анонсам, она появится уже не ранее чем с Java 9 в марте 2017 года как механизм взаимодействия модулей Jigsaw.

Благодаря принципу модульности подобные приложения можно обновлять в процессе их работы даже под нагрузкой. Те, кто не хочет дожидаться выхода свежих версий Java, могут воспользоваться модульным подходом для разработки приложений уже сейчас – технология носит название OSGi, ее первая спецификация появилась еще в начале 2000-х годов. Обновление кода приложений под нагрузкой без кластеризации может быть применимо как для PC, так и для мобильных устройств. Более того, это требует меньше ресурсов (процессорных, памяти, диска) и меньших временных затрат на настройку и тестирование.

А как же облака?


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

Таким образом, использование облачных серверов позволяет как разбивать модули приложения на отдельные узлы облака по принципу OSGi, так и запускать полноценное приложение со всеми модулями на каждом узле, создавая при этом избыточность. Какой из этих способов выбрать, зависит в первую очередь от ресурсов организации.

Каковы основные отличия спецификации OSGi от Jigsaw?


Само собой, чтобы понимать целесообразность использования спецификации OSGi с учетом грядущего обновления Java, следует разобраться, чем данные решения будут различаться и будут ли они совместимыми. Давайте рассмотрим таблицу ниже:
OSGi Jigsaw
Существует еще с 2000 года и является полностью рабочим механизмом с доказанной эффективностью Находится в разработке и не будет доступна до выхода Java 9
Является отдельной спецификацией Будет полностью встроена в платформу Java 9
Довольно сложна в использовании JPMS стремится быть проще в использовании, чем OSGi. Тем не менее, создание модульного продукта на основе немодульного и является основным источником сложности. Таким образом, Jigsaw вряд ли будет намного проще OSGi
Невозможно загружать внутренние классы модулей, так как они не видны извне. То есть загрузчик классов моего модуля может видеть только внутренние типы моего модуля, а также типы, которые были эксплицитно импортированы из других модулей Каждый тип видит любой другой тип, так как они находятся в одном и том же классе загрузчика. Правда, JPMS добавляет вторичную проверку, чтобы убедиться, что загружаемый класс имеет право доступа к типу, который он пытается загрузить. Внутренние типы из других модулей в реальности являются частными, даже если они объявлены публичными
Допускает одновременную работу нескольких версий приложений Не позволяет иметь несколько версий одного модуля одновременно
Существует управление жизненным циклом продукта Отсутствует управление жизненным циклом продукта
Динамическая модульность при развертывании в реальном времени Статическая модульность при развертывании
Разрешается иметь модули, содержание которых дублирует друг друга Невозможно иметь модули, содержание которых дублирует друг друга

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

В чем особенность работы со спецификацией OSGi?


Спецификация OSGi основана на использовании модульных приложений, компоненты которых работают в слабосвязанной среде, а маршруты взаимодействия между приложениями настраиваются в конфигурационном файле. Подобная модель приложения позволяет рассматривать его как сеть (граф), где узлы – компоненты, а ветви – связи между ними. При этом связи могут быть как односторонними, так и двухсторонними.

При обновлении кода подобной программы в первую очередь необходимо проанализировать следующие моменты:

  1. Какие узлы необходимо обновить.
  2. Какие ветви приложения будут затронуты.

Важно отметить, что выделяют два основных способа взаимодействия компонентов в распределенной изолированной среде: синхронный (посредством сервисов) и асинхронный (с использованием сообщений).

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

Анализ сервисов проводится посредством открытой реализации фреймворка OSGi, а анализ сообщений – с помощью прикладного кода (в нашем случае приложения Real Time Framework (RTF), речь о котором пойдет ниже). Это позволяет понять, в каких точках маршрута взаимодействия компонентов необходимо временно накопить в очереди сообщения (с помощью которых взаимодействуют компоненты) или временно приостановить вызываемые сервисы, чтобы сразу после обновления прикладного кода приложения можно было продолжить работу. В результате, пока код приложения будет обновляться до следующей версиии, сообщения будут накапливаться в очереди, а сервисы будут поставлены в режим ожидания, то есть на время обновления затронутые маршруты станут недоступны. Таким образом, сервер приложений не потребуется перезагружать, что позволит значительно уменьшить время недоступности приложений. Кроме того, не нужно снова загружать кэши, так как приложение продолжит работу ровно с того места, где приостановилось на время обновления.

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

Помимо этого, необходимо отделять API от реализации, чтобы можно было проще обновлять ее, не затрагивая API. Зачем? Для сравнения: сервер приложений обычно стартует за минуты, в идеальном случае – за 20–30 секунд, а обновление компонента в работающем сервере будет занимать всего 2–5 секунд, при этом все служебные данные не нужно будет снова загружать в память (кэш).

Приложение Real Тime Framework (RTF) для обновления кода под нагрузкой


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

Особенность RTF в том, что он расширяет возможности OSGi, позволяя не только обновлять исходный код приложения во время работы, но и делать это под нагрузкой. Другими словами, он позволяет «поставить приложение на паузу», обновить и продолжить выполнение запросов в штатном режиме уже на обновленном приложении.

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


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

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

Примеры использования RTF


Технология RTF не нова и уже не раз доказывала свою эффективность. Одним из примеров является настройка биллинговых сервисов «с нуля» для компании Axia на основе приложения RTF. Имплементация была выполнена standalone, т. е. без использования возможностей кластеризации. В данном случае Компании было как никогда важно обеспечить стабильную работу системы даже во время обновления каких-либо компонентов, так как специфика обработки платежей не позволяет допускать ни малейших ошибок или задержек в работе.

В последнее время большое количество разработок ведется для MANO (Management and Orchestration). В данном случае технология RTF применяется для работы системного монитора (следит за состоянием «железа») и монитора виртуализации сетей (VNF). Стоит отметить, что все работы ведутся в open stack, что еще раз подтверждает полную совместимость технологии RTF с облачными сервисами. В качестве примера стоит привести работу NEC/Netcracker по виртуализации сетей для японского оператора связи NTT DOCOMO. Этот проект дает компании возможность гибко изменять мощность базовой сети, а также позволяет значительно сократить время восстановления в случае сбоя оборудования.

Выводы


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

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

Комментарии (2)


  1. gkislin
    28.02.2017 18:48

    Сравнение OSGi c Jigsaw сильно напоминает рекламныю брошуру OSGi (Netcracker). Я полагаю надо все таки быть честными, раз пишете техническую статью. Jgisaw это же только механизм в Java, на основе которого можно уже строить различные фреймворки по динамической загрузке (если я это правильно понимаю). А что OSGi с 2000 года существует и далеко не майнстрим- так это плата за сложность обращения с ним. Вот если бы вы написали про его будующее, про возможности интеграции с Jigsaw- было бы интересно…


  1. insolite
    02.03.2017 23:15

    Кейс, который вы пытаетесь обосновать, не выглядит убедительным. Изначально вы апеллируете к потребности в бесперебойной и непрерывной работе систем, критичных для бизнеса. Такая потребность существует и действительно решается путем кластеризации. Далее для подкрепления своей позиции вы неоднократно упираете на «избыточность» такого решения. Простите, но кластеризация используется далеко не только для того, чтобы обеспечивать прозрачное обновление программного кода. Если говорить о HA-кластерах, то они в первую очередь обеспечивают fail-over. Избыточность здесь несомненно присутствует (степень ее зависит от топологии кластера), но она совершенно оправдана и необходима. Конечно, есть традиционные аппаратные решения для высокой доступности без кластеризации, но это абсолютно нишевые продукты с огромной стоимостью владения.

    Со всей очевидностью, «высоконагруженные системы, от адекватного функционирования которых зависит огромное число операций» должны иметь механизм для обеспечения fail-over. Этот механизм, который используется повсеместно и успешно — кластеризация. Поэтому никакой избыточности в плохом смысле здесь нет. Никто не городит кластер исключительно для горячего обновления компонентов. Кластер априори есть.

    Я слабо понял, на какую нишу вы претендуете. Не в том смысле, что проповедуют ваши маркетекторы, а где ваше решение действительно оправдано. Если исходить из описания, что вы предоставили, возникают серьезные сомнения в масштабируемости предлагаемой архитектуры. Извиняюсь за банальность, но масштабируемость бывает вертикальной и горизонтальной. Поскольку кластеризацию вы активно отрицаете, правильно ли я понимаю, что предлагается масштабироваться вертикально? Собственно, упоминание такого преимущества как разогретый кэш предполагает два варианта: или кэш существует в рамках одного процесса (одной JVM), или он распределенный (data grid, например). Первый вариант масшабируется в крайне ограниченных пределах. О втором варианте нет ни слова.

    Помимо этого, не могли бы вы прояснить следующие моменты:

    1. Что понимается под «поставить приложение на паузу»? О какой высокой доступности тогда идет речь?
    2. Насколько я понял, транспорт для отправки сообщений не использует персистентные очереди. Что произойдет в случает сбоя?