О большой практической пользе awk

Постановка задачи: есть Linux-сервер, на котором живёт сотня-другая виртуальных хостов. Сервер работает под управлением nginx. Нужно реализовать подсчет трафика с отображением "живой" статистики.

Вариант решения:

Таблица для хранения данных:

[-]
View Code MySQL
CREATE TABLE `traf_stats` (
    `in` INTEGER UNSIGNED NOT NULL, /* если качаются файлы размером более 4 ГБ, нужно использовать BIGINT */
    `out` INTEGER UNSIGNED NOT NULL,
    `sum` INTEGER UNSIGNED NOT NULL,
    `host` VARCHAR(64) NOT NULL,
    `dt` DATETIME NOT NULL,
    `ym` MEDIUMINT NOT NULL,
    KEY `host_ym`(`host`, `ym`)
);

Фрагменты конфигурационного файла nginx:

[-]
View Code Text
http {
    log_format trafctr '$request_length $bytes_sent $server_name $time_local';
}

Каждый виртуальный хост должен содержать подобную строку:

[-]
View Code Text
server {
    access_log /var/log/nginx/traffic_vhost trafctr;
}

В логе будут записи вида

[-]
View Code Text
1761 484 example.com 22/Jan/2009:20:05:46 -0500

А теперь собственно чёрная магия с использованием awk:

[-]
View Code Bash
#! /bin/sh

AWK=/usr/bin/awk
TAIL=/usr/bin/tail
MYSQL=/usr/bin/mysql
MYSQL_USER=mysql
MYSQL_PASS=pass
MYSQL_DB=traffic
LOG_PATH=/var/log/nginx

$TAIL -F $LOG_PATH/* | $AWK '
/^[[:digit:]]+ [[:digit:]]+ / {
        m["Jan"]="01";
        m["Feb"]="02";
        m["Mar"]="03";
        m["Apr"]="04";
        m["May"]="05";
        m["Jun"]="06";
        m["Jul"]="07";
        m["Aug"]="08";
        m["Sep"]="09";
        m["Oct"]="10";
        m["Nov"]="11";
        m["Dec"]="12";
        year=substr($4, 8, 4);
        month=m[substr($4, 4, 3)];
        day=substr($4, 1, 2);
        print "INSERT INTO traf_stats VALUES (" $1 ", " $2 ", " $1+$2 ", \"" $3 "\", \"" year "-" month "-" substr($4, 1, 2) " "substr($4, 13, 8) "\", " year month ");"; }
'
| $MYSQL -u$MYSQL_USER -p$MYSQL_PASS -D$MYSQL_DB

Удалось обойтись тремя лишними процессами: mysql, awk и tail (вместо пяти: mysql, awk, tail, egrep и sed).

Подсчёт трафика в nginx: часть 2.

Добавить в закладки

Связанные записи

23
Янв
2009

Комментарии к статье «Подсчет трафика в nginx» (9)  »

  1. japplegame says:

    Изящное решение.
    К сожалению недостаточно знаю mysql, посему возник вопрос:
    Если по каким либо причинам будет на некоторое время нарушена связь с сервером MySQL, то что произойдет с описываемой связкой? Она обвалится или просто будут пропущены логи за промежуток с нарушенной связью?

    • Vladimir says:

      Если честно, я не проверял — копаю сейчас в сторону написания своего модуля для nginx.

      Но по идее, в логах просто появится промежуток за время с нарушенной связью.

  2. japplegame says:

    Кстати, можно обойтись без awk, ведь можно задать log_format таким образом, чтобы сразу создать нужный SQL-запрос.
    А если потребуется какая-то сложная логика, можно воспользоваться хранимыми процедурами MySQL.

    • Vladimir says:

      С одной стороны, да. А с другой стороны — при большой посещаемости такой лог займет очень много места.

  3. japplegame says:

    И еще. Я проверил, утилита mysql не вываливается в случае обрыва соединения с сервером, а культурно пытается подконнектится при каждом запросе. Так что действительно, будут просто промежутки в БД.
    Что касаемо модуля для nginx, то у меня тоже возникла идея создать модуль на базе ngx_http_log_module. Добавить туда возможность записи лога в БД. Правда сразу появились вопросы. Дело в том, что при работе с БД могут возникнуть задержки, которые (вследствие архитектуры nginx) будут блокировать обработку ВСЕХ текущих запросов. Неприятно, однако. Правда, вероятно, есть возможность работать с MySQL в “неблокируещем” режиме, в частности, применять INSERT DELAYED. В итоге я рещил, что проще скрестить tail и mysql, написать маленький демон, который будет, скажем, раз в секунду читать лог nginx и писать его в БД.
    Тем не менее, даже просто ради приобретения опыта, я с удовольствием посодействую вам в создании этого модуля, начиная от тестирования и может быть каких-то идей, заканчивая собственно программированием (неплохо владею C и C++).

    • Vladimir says:

      INSERT DELAYED работает только с MyISAM,я не уверен, целесообразно ли использовать MyISAM при большой посещаемости ресурса.

      Просто уже неоднократно видел, как от большого числа вставок падают майисамовские таблицыю

      У меня была идея заставить модуль писать в сокет, на другом конце которого будет висеть демон и передавать запросы непосредственно MySQL (возможно, даже с некоторой обработкой данных).

      • japplegame says:

        Ну если именно так подходить к вопросу, то зачем вообще заморачиваться? Читать прямо из файла, без всяких сокетов.
        Можно еще отдельный поток (thread) создать, который и будет писать в MySQL. А чтобы сервер поменьше мучать, запросы накапливать в буфере и отправлять одним большим INSERTом. А в случае обрыва связи временно буферизовать логи в файле, как это делает mod_log_sql для апача.

        • Vladimir says:

          Потоки не особо кросс-платформенные. Плюс к тому, при сборке добавляется лишняя зависимость — libmysql15.

          Надо в коде nginx основательно покопаться…

  4. [...] статье “Подсчёт трафика в nginx” я приводил один из возможных вариантов живого [...]

Подписаться на RSS-ленту комментариев к статье «Подсчет трафика в nginx» Trackback URL: http://blog.sjinks.org.ua/mysql/488-counting-traffic-for-nginx/trackback/

Оставить комментарий к записи «Подсчет трафика в nginx»

Вы можете использовать данные тэги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Оставляя комментарий, Вы выражаете своё согласие с Правилами комментирования.

Подписаться, не комментируя