PHP Conference Japan 2024

应用性能监控 (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()来实现,分别用于全局注册订阅程序或与特定管理器注册。

<?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 );

?>
添加注释

用户贡献的注释

此页面没有用户贡献的注释。
To Top