Разработка и диагностика MBean

Создание Standard MBean

ERP использует стандартное соглашение JMX. Интерфейс должен:

  • быть публичным;

  • наследовать маркер ru.bgcrm.jmx.MBean;

  • называться <ImplementationClass>MBean с точным регистром;

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

interface QueueStateMBean : MBean {
    val size: Int
    val isAvailable: Boolean
}

class QueueState(
    private val queue: Queue<*>
) : QueueStateMBean {
    override val size: Int
        get() = queue.size

    override val isAvailable: Boolean
        get() = true
}

Kotlin-свойства будут опубликованы как атрибуты Size и Available. В Java используйте обычные getXxx()/isXxx() в интерфейсе QueueStateMBean.

Маркер MBean нужен для типизации MBeanHandler, но сам по себе не превращает произвольно названный интерфейс в Standard MBean. Соответствие имени интерфейса имени класса проверяет JMX.

Регистрация

Регистрируйте объект после MBeanHandler.init(), обычно при инициализации подсистемы:

MBeanHandler.register(
    mBean = QueueState(queue),
    type = "Queue",
    name = "notifications",
    description = "Notification delivery queue"
)

Результат:

BGCRM:type=Queue,name=notifications,description=Notification delivery queue

Для снятия используйте в точности те же три значения:

MBeanHandler.unregister(
    type = "Queue",
    name = "notifications",
    description = "Notification delivery queue"
)

unregister не проверяет server.jmx.pools. Если объекта нет, ошибка записывается в лог.

Требования к именам

  • type не должен быть пустым.

  • Выбирайте короткие стабильные машинные значения; не включайте секреты, URL, токены и пользовательские данные.

  • Не различайте объекты только символами *?:=," или переводом строки: builder удаляет их, и два значения могут превратиться в одно имя.

  • Считайте description частью идентичности. При её изменении отдельно снимите MBean со старым значением.

  • Для множества однотипных объектов оставляйте стабильный type, а экземпляр кодируйте в name.

Требования к атрибутам

JMX-клиент или exporter может читать атрибуты часто и последовательно. Getter должен быть потокобезопасным, ограниченным по времени и по возможности не выполнять I/O.

Если вычисление дорогое:

  1. перенесите сбор данных в фоновый процесс; или

  2. кешируйте результат и задайте понятный TTL; или

  3. исключите атрибут из массового scrape-конфига.

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

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

Проверка

Для handler-а и построителя имён уже есть тесты:

  • MBeanHandlerTest — выключение, повторная регистрация, снятие и ошибки JMX;

  • ObjectNameBuilderTest — формирование и очистка значений ObjectName;

  • тесты фабрик API-компонентов проверяют передаваемые type, name и description.

Для нового MBean добавьте тест, который регистрирует реализацию в настоящем локальном MBeanServer или вызывает JMX introspector. Такой тест обнаружит несоответствие пары <Class>/<Class>MBean, которое mock-тест handler-а не видит.

После запуска ERP проверьте:

  1. в логе есть MBean '<objectName>' successfully registered;

  2. объект появился в домене BGCRM;

  3. набор и типы атрибутов совпадают с интерфейсом;

  4. повторное чтение не создаёт заметную нагрузку;

  5. после пересоздания компонента значения относятся к новому экземпляру.

Диагностика

Симптом Что проверить

Домен BGCRM пуст

Значение server.jmx.pools, порядок вызова MBeanHandler.init(), наличие регистрации в логе

Нет одного ленивого MBean

Был ли инициализирован соответствующий Kotlin object или запрошен включённый API-компонент

NotCompliantMBeanException

Публичность интерфейса, точное имя <ImplementationClass>MBean, публичность getter-ов и поддерживаемые JMX-типы

Есть два похожих объекта

Не различается ли description; это часть полного ObjectName

Повторная регистрация иногда пропадает

Конкуренцию за одинаковое имя: внутренний lock ждёт 3 секунды и при таймауте не пишет сообщение

Scrape стал медленным

Время каждого getter-а и слишком широкое правило экспортера; не ограничивайтесь анализом домена BGCRM

Prometheus не видит атрибут

Конфигурацию JMX-exporter, преобразование имени и фильтры. Наличие атрибута сначала подтвердите через JConsole/JMX

Текущие технические долги

  • server.jmx.pools следует переименовать или разделить на общий флаг и флаги подсистем.

  • ObjectNameBuilder должен инициализировать логгер до обработки ошибочного имени.

  • ClientEngineFactoryCachesMbean требуется переименовать в ClientEngineFactoryCachesMBean и покрыть реальной JMX-регистрацией.

  • Для ожидания lock-а полезен warning, иначе пропуск регистрации не диагностируется.