用于时间测量探查器服务的设计模式


What design pattern to use for a time-measurement profiler service?

我有一个symfony2应用程序。它抽象了一堆外部 API,所有这些 API 都实现了 ExternalApiInterface

每个ExternalApiInterface都有很多方法,例如 fetchFoofetchBar.

现在,我想编写一个服务来测量ExternalApiInterface实例的每个方法调用的时间。

我目前的想法是实现一个StopWatchExternalApiDecorator,它包装每个方法调用。然而,在我的理解中,这种方法会导致代码重复。

我想我将使用StopWatch组件进行时间测量,但这感觉很奇怪:

class StopWatchExternalApiDecorator implements ExternalApiInterface {
    public function __construct(ExternalApiInterface $api, Stopwatch $stopWatch)
    {
       $this->api = $api;
       $this->stopWatch = $stopWatch;
    }
    public function fetchBar() {
         $this->stopWatch->start('fetchBar');
         $this->api->fetchBar()
         $this->stopWatch->stop('fetchBar');
    }
    public function fetchFoo() {
         $this->stopWatch->start('fetchFoo');
         $this->api->fetchFoo()
         $this->stopWatch->stop('fetchFoo');
    }
}

似乎我正在伤害 DNRY(不要重复自己)的方法。我是否为此类问题使用了正确的模式,还是还有其他更合适的模式?从某种意义上说,更合适:在一个地方进行所有测量,并且没有代码重复。

我也不喜欢必须触摸装饰器,以防界面中有新方法。在我看来,这应该是独立的。

我正在考虑我处理的一些 API,它使用一个通用函数进行调用和一个方法参数

这里有一些非常基本的伪代码

public function call($method = 'fetchBar',$params=array()){
    $this->stopWatch->start($method);
    $this->{"$method"}($params);
    $this->stopWatch->stop($method);
}
private function fetchBar(){
    echo "yo";
}

也许这会有所帮助

我采用了装饰器方法,只是在不同的层面上。

在我的架构中,api服务使用的是HttpClientInterface,每个请求最终都通过调用doRequest来处理。因此,装饰器在没有代码重复的情况下最有意义:

<?php
namespace Kopernikus'BookingService'Component'Http'Client;
use Kopernikus'BookingService'Component'Performance'PerformanceEntry;
use Psr'Http'Message'RequestInterface;
use Psr'Http'Message'ResponseInterface;
use Psr'Log'LoggerInterface;
use Symfony'Component'Stopwatch'Stopwatch;
/**
 * ProfileClientDecorator
 **/
class ProfileClientDecorator implements HttpClientInterface
{
    /**
     * @var Stopwatch
     */
    private $stopwatch;
    /**
     * @var HttpClientInterface
     */
    private $client;
    /**
     * @var LoggerInterface
     */
    private $logger;
    /**
     * ProfileClientDecorator constructor.
     * @param HttpClientInterface $client
     * @param Stopwatch           $stopwatch
     * @param LoggerInterface     $logger
     */
    public function __construct(HttpClientInterface $client, Stopwatch $stopwatch, LoggerInterface $logger)
    {
        $this->client = $client;
        $this->stopwatch = $stopwatch;
        $this->logger = $logger;
    }
    /**
     * @param RequestInterface $request
     *
     * @return ResponseInterface
     */
    public function doRequest(RequestInterface $request)
    {
        $method =  $request->getMethod();
        $response = $this->doMeasuredRequest($request, $method);
        $performance = $this->getPerformance($method);
        $this->logPerformance($performance);
        return $response;
    }
    /**
     * @param RequestInterface $request
     * @param string           $method
     *
     * @return ResponseInterface
     */
    protected function doMeasuredRequest(RequestInterface $request, $method)
    {
        $this->stopwatch->start($method);
        $response = $this->client->doRequest($request);
        $this->stopwatch->stop($method);
        return $response;
    }
    /**
     * @param $method
     * @return PerformanceEntry
     */
    protected function getPerformance($method)
    {
        $event = $this->stopwatch->getEvent($method);
        $duration = $event->getDuration();
        return new PerformanceEntry($duration, $method);
    }
    /**
     * @param PerformanceEntry $performance
     */
    protected function logPerformance(PerformanceEntry $performance)
    {
        $context = [
            'performance' => [
                'duration_in_ms'       => $performance->getDurationInMs(),
                'request_name' => $performance->getRequestName(),
            ],
        ];
        $this->logger->info(
            "The request {$performance->getRequestName()} took {$performance->getDurationInMs()} ms",
            $context
        );
    }
}

在我的services.yml

performance_client_decorator:
    class: Kopernikus'Component'Http'Client'ProfileClientDecorator
        decorates: http.guzzle_client
            arguments:
                - @performance_client_decorator.inner
                - @stopwatch
                - @logger