此扩展包含一个事件订阅程序 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()来实现,分别用于全局注册订阅程序或与特定管理器注册。
<?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://127.0.0.1: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 );
?>