ΠœΠΎΠ½ΠΈΡ‚ΠΎΡ€ΠΈΠ½Π³ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ прилоТСния (Application Performance Monitoring, ΠΈΠ»ΠΈ APM)

ΠœΠΎΠ΄ΡƒΠ»ΡŒ содСрТит 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(), Π° для ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΎΠ³ΠΎ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° класса Manager β€” ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠΌ MongoDB\Driver\Manager::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): void
{
if (
'find' === $event->getCommandName()) {
$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);

?>
οΌ‹Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ

ΠŸΡ€ΠΈΠΌΠ΅Ρ‡Π°Π½ΠΈΡ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΉ

ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΠΈ Π΅Ρ‰Ρ‘ Π½Π΅ добавляли примСчания для страницы