该扩展包含一个事件订阅 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 和 commandSucceeded)相关联,每个事件对象都公开了一个 requestId
字段。
为了记录每个查询形状的平均时间,我们将从检查 commandStarted 事件中的 find
命令开始。然后,我们将向 pendingCommands
属性添加一个项目,该项目由其 requestId
索引,其值表示查询形状。
如果我们接收到具有相同 requestId
的相应 commandSucceeded 事件,我们将把事件的持续时间(来自 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, " (", $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 );
?>