Мониторинг производительности приложения (Application Performance Monitoring или APM)
Начиная с версии 1.3, драйвер MongoDB содержит API для мониторинга производительности.
Это API позволяет определить, как долго выполняется конкретная операция, путем
настройки подписчиков. Каждый подписчик должен реализовывать один или
более интерфейсов из пространства имен MongoDB\Driver\Monitoring
.
В данный момент доступен только один интерфейс -
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 )
{
}
public function commandSucceeded( \MongoDB\Driver\Monitoring\CommandSucceededEvent $event )
{
}
public function commandFailed( \MongoDB\Driver\Monitoring\CommandFailedEvent $event )
{
}
}
?>
Регистрация подписчика
Как только объект подписчик создан, необходимо его зарегистрировать в драйвере в системе мониторинга. Регистрация производится методом MongoDB\Driver\Monitoring\addSubscriber().
<?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 )
{
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 )
{
$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 )
{
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 );
?>