CakeFest 2024: The Official CakePHP Conference

Мониторинг производительности приложения (Application Performance Monitoring или APM)

Драйвер MongoDB содержит API подписчика событий, который позволяет приложениям отслеживать команды и внутреннюю активность, относящуюся к » Спецификации обнаружения и мониторинга серверов. В данном руководстве будет продемонстрирован мониторинг команд с помощью интерфейса MongoDB\Driver\Monitoring\CommandSubscriber.

Интерфейс MongoDB\Driver\Monitoring\CommandSubscriber определяет три метода: commandStarted, commandSucceeded и commandFailed. Каждый из них принимает один параметр event класса, соответствующего нужному событию. К примеру, commandSucceeded принимает аргумент $event класса MongoDB\Driver\Monitoring\CommandSucceededEvent.

В данном руководстве вы реализуем подписчика, который создаёт список профилировок всех запросов и среднего времени их исполнения.

Класс подписчик Scaffolding

Мы начнём с шаблона для нашего подписчика:

<?php

class QueryTimeCollector implements \MongoDB\Driver\Monitoring\CommandSubscriber
{
public function
commandStarted( \MongoDB\Driver\Monitoring\CommandStartedEvent $event ): void
{
}

public function
commandSucceeded( \MongoDB\Driver\Monitoring\CommandSucceededEvent $event ): void
{
}

public function
commandFailed( \MongoDB\Driver\Monitoring\CommandFailedEvent $event ): void
{
}
}

?>

Регистрация подписчика

Как только объект подписчик создан, необходимо его зарегистрировать в драйвере в системе мониторинга. Регистрация производится методом MongoDB\Driver\Monitoring\addSubscriber() или MongoDB\Driver\Manager::addSubscriber() для регистрации подписчика глобально или с помощью определённого класса Manager соответственно.

<?php

\MongoDB\Driver\Monitoring\addSubscriber
( new QueryTimeCollector() );

?>

Реализуем логику

Теперь займёмся реализацией логики класа подписчика. Для сопоставления двух событий, относящихся к успешно выполненной команды (commandStarted and commandSucceeded), каждый объект события предоставляет поле requestId.

Для записи среднего времени выполнения запроса мы начнём с отслеживания команды find в событии commandStarted. Мы будем добавлять элемент в массив pendingCommands с индексом соответствующим requestId и значением, соответствующим запросу.

Когда мы получим соответствующее событие commandSucceeded с соответствующим requestId, мы добавим время выполнения (из durationMicros) к общему времени и увеличим счётчик операций.

Если мы получим событие commandFailed, мы просто удалим соответствующую запись из pendingCommands.

<?php

class QueryTimeCollector implements \MongoDB\Driver\Monitoring\CommandSubscriber
{
private
$pendingCommands = [];
private
$queryShapeStats = [];

/* Создаёт форму запроса из аргумента фильтра. В данный момент учитываются
* только поля верхнего уровня. */
private function createQueryShape( array $filter )
{
return
json_encode( array_keys( $filter ) );
}

public function
commandStarted( \MongoDB\Driver\Monitoring\CommandStartedEvent $event ): void
{
if (
array_key_exists( 'find', (array) $event->getCommand() ) )
{
$queryShape = $this->createQueryShape( (array) $event->getCommand()->filter );
$this->pendingCommands[$event->getRequestId()] = $queryShape;
}
}

public function
commandSucceeded( \MongoDB\Driver\Monitoring\CommandSucceededEvent $event ): void
{
$requestId = $event->getRequestId();
if (
array_key_exists( $requestId, $this->pendingCommands ) )
{
$this->queryShapeStats[$this->pendingCommands[$requestId]]['count']++;
$this->queryShapeStats[$this->pendingCommands[$requestId]]['duration'] += $event->getDurationMicros();
unset(
$this->pendingCommands[$requestId] );
}
}

public function
commandFailed( \MongoDB\Driver\Monitoring\CommandFailedEvent $event ): void
{
if (
array_key_exists( $event->getRequestId(), $this->pendingCommands ) )
{
unset(
$this->pendingCommands[$event->getRequestId()] );
}
}

public function
__destruct()
{
foreach(
$this->queryShapeStats as $shape => $stats )
{
echo
"Shape: ", $shape, " (", $stats['count'], ")\n ",
$stats['duration'] / $stats['count'], "µs\n\n";
}
}
}

$m = new \MongoDB\Driver\Manager( 'mongodb://localhost:27016' );

/* Добавляем подписчика */
\MongoDB\Driver\Monitoring\addSubscriber( new QueryTimeCollector() );

/* Запускаем пачку запросов */
$query = new \MongoDB\Driver\Query( [
'region_slug' => 'scotland-highlands', 'age' => [ '$gte' => 20 ]
] );
$cursor = $m->executeQuery( 'dramio.whisky', $query );

$query = new \MongoDB\Driver\Query( [
'region_slug' => 'scotland-lowlands', 'age' => [ '$gte' => 15 ]
] );
$cursor = $m->executeQuery( 'dramio.whisky', $query );

$query = new \MongoDB\Driver\Query( [ 'region_slug' => 'scotland-lowlands' ] );
$cursor = $m->executeQuery( 'dramio.whisky', $query );

?>
add a note

User Contributed Notes

There are no user contributed notes for this page.
To Top