Knock

Самый простой способ мониторинга вашего сайта без лишних затрат

Начать бесплатно

Метрики для сайта. Реализация на PHP

Сбор метрик в вашем веб-приложении или сайте - очень важное и увлекательное занятие. Это способ заглянуть внутрь вашего приложения и убедиться, что все работает как надо. Метрики позволяют крепко спать по ночам и вовремя реагировать, когда что-то пошло не так.

Инструменты

Есть множество способов сбора метрик. Их можно получать, например, с самого веб-сервера, можно получать метрики из операционной системы и т.д. Для всего этого написано целая куча всяческих инструментов. Но больше всего нас интересуют бизнес метрики.

Сколько пользователей пошли по тому или иному пути? Сколько запросов к стороннему ресурсу мы выполняем? Какое время работы того или иного куска кода? Чтобы ответить на подобные вопросы вам придется встраивать метрики непосредственно в свое приложение, в свой сайт.

Для начала, определимся с выбором хранилища для наших метрик. Нам нужна база данных для временных рядов. Их существует великое множество. Есть честные базы, такие как influxdb, есть новы и молодежные проекты, такие как prometheus, а есть древние артефакты, такие как graphite.

Не хочется вдаваться в споры и холивары какая из указанных выше систем луч Для нашего проекта мы возьмем influx. Это довольно функциональная система с кучей плагинов и дополнений которые нам пригодятся. Influx очень похожа на обычную sqlную базу данных, вроде MySQL, поэтому у вас навряд ли возникнут трудности при ее установке и настройке.

Кроме самой базы, ребята из influx написали замечательный инструмент - telegraf. С его помощью можно собирать данные из самых разных мест и записывать все это в базу. Его мы тоже будем использовать для мониторинга сайта.

Использование

Прежде всего, устанавливаем influx и telegraf. Telegraf будет ходить за метриками на наш сайт и складывать их в базу. Для этого его нужно правильно настроить.

[[inputs.httpjson]]
   ## NOTE This plugin only reads numerical measurements, strings and booleans
   ## will be ignored.

   ## a name for the service being polled
   name = "webserver_stats"
 
   ## URL of each server in the service's cluster
   servers = [
     "http://localhost:9999/stats/",
     "http://localhost:9998/stats/",
   ]
   ## Set response_timeout (default 5 seconds)
   response_timeout = "5s"

   ## HTTP method to use: GET or POST (case-sensitive)
   method = "GET"

   ## List of tag names to extract from top-level of JSON server response
   # tag_keys = [
   #   "my_tag_1",
   #   "my_tag_2"
   # ]

   ## HTTP parameters (all values must be strings)
   [inputs.httpjson.parameters]
     event_type = "cpu_spike"
     threshold = "0.75"

   ## HTTP Header parameters (all values must be strings)
   # [inputs.httpjson.headers]
   #   X-Auth-Token = "my-xauth-token"
   #   apiVersion = "v1"

   ## Optional SSL Config
   # ssl_ca = "/etc/telegraf/ca.pem"
   # ssl_cert = "/etc/telegraf/cert.pem"
   # ssl_key = "/etc/telegraf/key.pem"
   ## Use SSL but skip chain & host verification
   # insecure_skip_verify = false

В этом конфиге http://localhost:9999/stats/ это адрес сайта, с которого мы будем снимать метрики. Можно указать что-то вроде http://localhost:9999/metrics.php. Чтобы telegraf мог правильно распарсить метрики, они должны быть в правильном формате. Для забора этих метрик будет использоваться плагин httpjson. JSON на странице с метриками будет выглядеть примерно так:

{
    "counter1": 1,
    "counter2": 3
}

Как видите, ничего сложного. Для начала будем работать с простыми счетчиками. И прям тут нас уже ждут проблемы. Как в php сделать счетчики, чтобы они сохраняли свое состояние между запросами? Конечно, можно писать в базу или файл, но как-то это слишком много для инкремента числа. Если бы мы писали, например, на C, то могли бы не парится по этому поводу и просто держать счетчики на куче. Но тут нам придется хитрить.

Если немного подумать, то можно вспомнить про кеш. Например, можем использовать memcached. Но мы сделаем все еще проще - воспользуемся apc(или apcu). Apc позволить хранить счетчики в памяти и иметь к ним доступ из любого процесса в fpm пуле.

Для начала реализуем базовый класс для наших метрик

<?php

namespace Ruler;


abstract class Metrics
{
    public $name = "";

    public function __construct($name)
    {
        $this->name = $name;
    }

    abstract public function update($val = null);
    
    abstract public function get(array $params = []);
}

В конструкторе мы указываем название метрики, которое в будущем будет использоваться для выборки значений из influxdb.

Теперь реализуем класс счетчика, который хранит свое значение в apc:

<?php

namespace Ruler\Metrics;

use Ruler\Metrics;


class Counter extends Metrics
{

    public function update($val = null)
    {
        if (!apcu_add($this->name, 1)) {
            apcu_inc($this->name);
        }

        $val = apcu_fetch($this->name);

        if ($val >= PHP_INT_MAX) {
            $val = 1;
            apcu_store($this->name, $val);
        }

        return $val;
    }

    public function get(array $params = [])
    {
        $result = false;
        $value =  apcu_fetch($this->name, $result);

        return $result?$value:0;
    }

    public function set($val)
    {
        apcu_store($this->name, $val);
    }
}

Все готово, мы можем использовать наш счетчик. Предположим, что мы хотим посчитать количество запросов в секунду к определенной странице нашего сайта. На этой странице нужно разместить код, вида:

use Ruler\Metrics\Counter;

$counter = new Counter("counter1");
$counter->update();

И теперь на странице metrics.php (которую мы указали в настройках telegraf) вывдоим значение нашего счетчика:

use Ruler\Metrics\Counter;

$counter = new Counter("counter1");
echo json_encode([
	"counter1" => $counter->get()
]);

Все наши данные попадут в influx в базу данных с названием telegraf в measurement(табличка, если так удобней) с названием вида httpjson_webserver_stats. В названии таблички httpjson это префикс по умолчанию, а webserver_stats это настройка name которую вы указали в конфиге telegraf.

Для визуализации данных можно использовать grafana, которая прекрасно умеет работать с influx.

Пример запроса в influx, который выведет количество запросов в секуду

SELECT non_negative_derivative("counter1", 1s) 
FROM "httpjson_webserver_stats" WHERE $timeFilter GROUP BY "server"

Использованием non_negative_derivative позволяет определить насколько быстро растет наш счетчик, что нам и нужно.