четверг, 12 марта 2015 г.

[prog.sobjectizer] Низкоуровневый мониторинг: делать или не делать?

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

К разработке прикладных систем, построенных из совокупности разнородных акторов/агентов, имею отношение очень давно. Где-то с конца 1990-х точно. За это время прочно утвердился в мысли о том, что мониторинг собранного из агентов приложения (особенно предназначенного для режима 24x7) -- это одна из основополагающих вещей. Не будет вменяемого мониторинга, невозможно будет нормально обслуживать прикладную систему.

Поэтому в свое время, еще для SObjectizer-4, была написана небольшая библиотека gemont (generic monitoring tools), предоставлявшая программисту небольшой набор инструментов для встраивания в свои агенты т.н. "источников данных". Источники данных -- это именованные объекты, при обновлении значения которого внутри приложения генерировалось сообщение об обновлении объекта. Это сообщение могло быть обработано прямо здесь или же его можно было оттранслировать во внешний мир (например, в специализированную мониторинговую консоль).

Gemont, конечно, в изрядной степени стал следствием NIH-синдрома и местами производил впечатление трехколесного, собранного из чего попало, велосипеда. Но зато пользоваться им было просто. По сравнению с gemont-овскими источниками данных подключение к приложению средств раздачи мониторинговой информации через SNMP был геморроем в весьма запущенной стадии.

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

Так, программист мог через gemont-овские источники данных показывать количество обрабатываемых агентом транзакций, описание текущего действия, времена выполнения операций. Мы у себя сделали такие источники данных, которые, например, показывали время выполнения последней операции, а так же минимальное и максимальное время за N последних операций + усредненное время за этот же отрезок. Что позволяло оперативно оценивать состояние приложения, а в ряде случаев и сразу же находить проблемные места. Поэтому первым делом при жалобе от техподдержки на странное поведение приложения было подключение к приложению посредством мониторинговой консоли и анализ значений источников данных.

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

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

Ключевой момент в том, что все эти источники данных были расставлены по приложению вручную. Поэтому мониторинг отъедал ресурсы только там, где это было нужно. В компоненте мог быть десяток агентов с источниками данных внутри. А рядом еще десяток-другой агентов калибром поменьше, в которых мониторинга не было вовсе. И, насколько я помню, это особо не напрягало. Ну не известно, сколько сообщений стоит в очереди к агенту, ну и не страшно. Значит агент такой, что следить за ним особо и не нужно.

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

Одной из таких примочек является низкоуровневый мониторинг на уровне внутренностей SObjectizer-овского Run-Time.

Только вот терзают смутные сомнения.

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

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

Насколько я понимаю, в современных условиях, когда разнообразный сервер-сайд -- это давно уже вотчина менеджед-кода (начиная от не шибко быстрых Ruby и Erlang, заканчивая Akka под JVM). И нативный C++ный агентный фреймворк если и нужен здесь, то разве что по двум причинам: из-за интеграции с большим объемом нативного легаси кода и/или из-за более высоких требований по скорости/экономному расходу ресурсов. Т.е. если на сервер-сайде будет применяться C++ный агентный фреймворк, но не в последнюю очередь из-за своего быстродействия.

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

Посему мне представляется, что низкоуровневый мониторинг должен быть не просто опциональным. А настолько опциональным, что когда он не используется, то от него вообще никаких следов нет. Замеров пока не проводил, но по опыту, даже конструкция вида:

...
if(monitoring_enabled)
  increment_queue_length_data_source();
...

где-то в дебрях механизма диспетчеризации сообщений на скоростях в районе 10M msg/sec будет снижать пропускную способность на сотню-другую тысяч msg/sec. Да, на таких масштабах эти потери будут составлять всего лишь проценты. Но для чего использовать нативный язык и фреймворк для него, если не для экономии этих самых процентов? (На самом деле, конечно, C++ хорош для решения сложных задач не только из-за своего быстродействия, но это тема отдельного флейма).

Посему хочется, чтобы в случае отсутствия мониторинга, в коде не было никаких if(monitoring_enabled) или даже косвенных вызовов соответствующих фрагментов кода через виртуальные методы или указатели на функции. А это ведет, фактически, к наличию нескольких реализаций различных сущностей SObjectizer-а: с мониторингом и без оного. Например, реализация one_thread-диспетчера с мониторингом, а рядом реализация без мониторинга.

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

Основная проблема -- это использование мониторинга внутри приложения. Т.е. приложение должно понимать, нужно ли ему создавать копию one_thread-диспетчера с мониторингом внутри (т.е. более медленный вариант) или же максимально быстрый one_thread-диспетчер без мониторинга. Или же какой-то промежуточный вариант, в котором мониториг включается/выключается прямо в run-time.

Насколько все это будет востребовано? Насколько все это будет удобно?

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

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

Вот такие пока мысли. Буду думать дальше.

Комментариев нет: