Драйвер MongoDB содержит API подписчика событий, который позволяет приложениям отслеживать команды и внутреннюю активность, относящуюся к » Спецификации обнаружения и мониторинга серверов. В данном руководстве будет продемонстрирован мониторинг команд с помощью интерфейса MongoDB\Driver\Monitoring\CommandSubscriber.
Интерфейс MongoDB\Driver\Monitoring\CommandSubscriber
определяет три метода: commandStarted
,
commandSucceeded
и commandFailed
.
Каждый из них принимает один параметр event
класса,
соответствующего нужному событию. К примеру, commandSucceeded
принимает аргумент $event
класса
MongoDB\Driver\Monitoring\CommandSucceededEvent.
В данном руководстве вы реализуем подписчика, который создаёт список профилировок всех запросов и среднего времени их исполнения.
Мы начнём с шаблона для нашего подписчика:
<?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 );
?>