MongoDB 애플리케이션 성능 모니터링(APM)
버전 1.3부터 MongoDB 드라이버에는 성능 모니터링을 위한 API가 포함되어 있습니다. 이 API를 사용하면 구독자를 설정하여 특정 작업에 걸리는 시간을 알 수 있습니다. 각 구독자는 MongoDB\Driver\Monitoring
네임스페이스 아래에 하나 이상의 인터페이스를 구현해야 합니다. 현재 MongoDB\Driver\Monitoring\CommandSubscriber 인터페이스만 사용할 수 있습니다.
MongoDB\Driver\Monitoring\CommandSubscriber 인터페이스는 commandStarted
,
commandSucceeded
및 commandFailed
의 세 가지 메서드를 정의합니다. 이 세 가지 메서드 각각은 해당 이벤트에 대한 특정 클래스의 단일 event
인수를 허용합니다. 예를 들어 commandSucceeded
의 $event
인수는 MongoDB\Driver\Monitoring\CommandSucceededEvent 개체입니다.
이 튜토리얼에서는 모든 쿼리 프로필과 평균 소요 시간 목록을 생성하는 구독자를 구현합니다.
Subscriber Class Scaffolding
구독자를 위한 프레임워크로 시작합니다.
<?php
class QueryTimeCollector implements \MongoDB\Driver\Monitoring\CommandSubscriber
{
public function commandStarted( \MongoDB\Driver\Monitoring\CommandStartedEvent $event )
{
}
public function commandSucceeded( \MongoDB\Driver\Monitoring\CommandSucceededEvent $event )
{
}
public function commandFailed( \MongoDB\Driver\Monitoring\CommandFailedEvent $event )
{
}
}
?>
Registering the Subscriber
가입자 개체가 인스턴스화되면 드라이버의 모니터링 시스템에 등록해야 합니다. 이는 MongoDB\Driver\Monitoring\addSubscriber() 또는 MongoDB\Driver\Manager::addSubscriber()를 호출하여 구독자를 전역적으로 또는 특정 Manager에 각각 등록함으로써 수행됩니다.
<?php
\MongoDB\Driver\Monitoring\addSubscriber( new QueryTimeCollector() );
?>
Implementing the Logic
등록된 개체와 함께 남은 유일한 것은 구독자 클래스에서 논리를 구현하는 것입니다. 성공적으로 실행된 명령(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 )
{
if ( array_key_exists( 'find', (array) $event->getCommand() ) )
{
$queryShape = $this->createQueryShape( (array) $event->getCommand()->filter );
$this->pendingCommands[$event->getRequestId()] = $queryShape;
}
}
public function commandSucceeded( \MongoDB\Driver\Monitoring\CommandSucceededEvent $event )
{
$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 )
{
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' );
/* 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 );
?>