2024 年日本 PHP 研討會

應用程式效能監控 (APM)

此擴充功能包含一個事件訂閱者 API,允許應用程式監控與「伺服器探索和監控規範」相關的命令和內部活動。» 伺服器探索和監控規範。本教學課程將示範如何使用 MongoDB\Driver\Monitoring\CommandSubscriber 介面進行命令監控。

MongoDB\Driver\Monitoring\CommandSubscriber 介面定義了三個方法:commandStartedcommandSucceededcommandFailed。這三個方法都接受一個 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 和 commandSucceeded),每個事件物件都會公開一個 requestId 欄位。

為了記錄每個查詢形狀的平均時間,我們將首先在 commandStarted 事件中檢查 find 指令。然後,我們會將一個項目新增到以其 requestId 作為索引的 pendingCommands 屬性中,其值代表查詢形狀。

如果我們收到具有相同 requestId 的對應 commandSucceeded 事件,我們會將事件的持續時間(來自 durationMicros)新增到總時間,並增加操作計數。

如果遇到對應的 commandFailed 事件,我們只需從 pendingCommands 屬性中移除該項目。

<?php

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

/* Creates a query shape out of the filter argument. Right now it only
* takes the top level fields into account */
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://127.0.0.1:27016' );

/* Add the subscriber */
\MongoDB\Driver\Monitoring\addSubscriber( new QueryTimeCollector() );

/* Do a bunch of queries */
$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 );

?>
新增筆記

使用者貢獻的筆記

此頁面尚無使用者貢獻的筆記。
To Top