habrahabr — Блог Валерия Леонтьева https://valera.ws Место публикации личных заметок. Технологии, управление, бизнес, жизнь Wed, 18 Mar 2009 12:38:41 +0000 ru-RU hourly 1 https://wordpress.org/?v=5.6.2 https://valera.ws/wp-content/uploads/2020/02/favicon.png habrahabr — Блог Валерия Леонтьева https://valera.ws 32 32 Фокусы с nginx https://valera.ws/2009.01.26~nginx-magic/ https://valera.ws/2009.01.26~nginx-magic/#comments Mon, 26 Jan 2009 14:23:05 +0000 http://valera.ws/?p=276 Читать далее Фокусы с nginx ]]> Вчерашний вечер я посвятил возне с http-сервером nginx в качестве фронтэнда к apache. Как известно, nginx — легковесный надежный HTTP-сервер, написанный Игорем Сысоевым (сотрудником Rambler). Он отлично подходит для выдачи статических страниц, особенно под нагрузкой. Обычно настраивается связка nginx+apache, в которой nginx обслуживает все входящие на сервер запросы, статические файлы отдает своими силами, а запросы на динамическое содержимое проксирует на apache.

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

Моя конфигурация

Для начала, что собственно требовалось сделать? Сервер настраивался для Хаброметра. Он должен был выдавать статику (лого и css) и динамику (собственно страницы сайта и png-хаброметры). При этом, надо было учесть, что хаброметр создается на лету в том случае, если не лежит в кэше (а кэш чистится каждые 2 часа при запросе новых данных). Страницы сайта также необходимо кэшировать. Вот такая была задача.

Реализацию решено было сделать следующим образом. nginx при обработке запроса должен следовать следующим правилам:

  1. Если запрошена статика, то просто вернуть ее (вся статика в папочке stuff).
  2. Если запрошена страница сайта, нужно проверить кэш; если в кэше файл не найден, передать запрос бэкэнду apache. Кэш страниц должен чиститься с заданной частотой (для разных страниц частота разная).
  3. Если запрошен информер, то нужно проверить кэш на наличие файла. Если файла там нет, передать запрос бэкэнду.

Для кэширования хаброметров выбрана файловая система. Все сгенерированные информеры складываются в каталог /image_cache/ и он чистится каждые 2 часа при обновлении исходных данных. Рисуются информеры и кладутся в этот каталог PHP-скриптом при соответствующем запросе.

Для кэширования страниц сайта выбран memcache, т.к. с ним легко и удобно работать (как из nginx, так и из PHP) и он сам может чистить кэшированные странички через заданный интервал времени, что ФС делать не может без дополнительных скриптов. Да и работать memcache будет побыстрее, т.к. все добро складируется в оперативке.

Получилась следующая конфигурация сервера:

# cat /etc/nginx/nginx.conf

user www-data;
worker_processes 2;
error_log  /var/log/nginx/error.log;
pid        /var/run/nginx.pid;
events {
    worker_connections  2048;
}
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    access_log  /var/log/nginx/access.log;
    sendfile        on;
    keepalive_timeout  65;
    tcp_nodelay        on;
    gzip  on;
    add_header Habrometr "hacker_mode_enabled;)";
    server {
        listen       80;
        server_name  habrometr.server.valera.ws habrometr.ru www.habrometr.ru;
        access_log  /var/log/nginx/habrometr.access.log;
        location / {
            root   /home/habrometr/public_html;
            index  index.html index.htm;
            if (-f $document_root/image_cache${uri}) {
                rewrite ^.*$ /image_cache/$uri last;
                break;
            }

            set $memcached_key "habrometr$uri";
            memcached_pass localhost:11211;
            # если в memcached не найден ресурс, передаем запрос на апач
            error_page 404 502 504 = @backend;
            add_header Content-Type "text/html; charset=UTF-8";
            gzip on;
            gzip_proxied any;
            gzip_types application/octet-stream;
    }
    location @backend {
        set $proxy_uri http://habrometr.ru:99999$request_uri;
        proxy_pass $proxy_uri;
        proxy_redirect off;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X_Forwarded-For $proxy_add_x_forwarded_for;
        proxy_connect_timeout 20;
    }
    location /image_cache/ {
        root   /home/habrometr/public_html;
        expires modified +2h; # кэш истекает через 2 часа после модификации файла
    }
    location /stuff/ {
        root   /home/habrometr/public_html;
        expires 30d;
    }
    location ~ /\.ht {
        deny  all;
    }
}

Учитывая приведенный выше сценарий, вся конфигурация должна быть понятна. Замечу лишь, что http://habrometr.ru:99999 — это apache, которому будут перенаправляться запросы. Порт я, конечно, изменил, в реальности обычно используют 8080 или что-то типо того.

Фокусы

А теперь о том, что нетривиального в этой конфигурации (во всяком случае для новичка в этой области).

Версия

Во-первых, сервер у меня работает на Debian 4.0. Весь софт я естественно ставил из стандартных репозиториев. Поставил оттуда и nginx. Установленный nginx оказался версии 0.4 при наличии последней версии 0.7 со значительным списком изменений.

Выяснилось, что версия 0.4 не умеет делать много из того, что было нужно. В частности:

  1. флаг modified не сузествует для директивы expire, а мне это необходимо было для указания времени устаревания кэша информеров (2 часа после создания: expire modified +2h);
  2. proxy_pass не умел использовать переменные, а мне требовалась эта возможность;
  3. memcached не использовал переменную $memcached_key для определения ключа, т.е. нельзя было задать ключ нужного формата.

В принципе, все эти проблемы можно было решить обходными извращенскими путями, но делать этого совсем не хотелось, по этому я просто поставил свежую версию nginx из сырцов. Благо, делается это очень просто.

Перед тем, как описать процесс установки, замечу, что по умолчанию при сборке из исходников все файлы nginx складывает в каталог /usr/local/nginx. Его, конечно, можно изменить (—prefix=). Но обратите внимание, что установленных из пакетов nginx раскидывает свои файлы по соответствующим каталогам системы (/etc, /var/log, /var/run и т.д.), что лично мне определенно нравится больше, чем /usr/local/nginx/*. По этому я откомпилировал nginx из сырцов с настройками на системные каталоги, а потом вместо make install просто вручную заменил старый бинарный файл сервера в каталоге /usr/sbin на новый (/usr/sbin/nginx). Больше значимых файлов после сборки для сервера нет. Конфиг, естественно, остается тот же самый.

Итак, установка nginx на Debian etch из исходников поверх установленного пакета старой версии.

# wget http://sysoev.ru/nginx/nginx-0.7.31.tar.gz
# tar xzf nginx-0.7.31.tar.gz
# cd nginx-0.7.31
# apt-get install libpcre3 libpcre3-dev libpcrecpp0
# /etc/init.d/nginx stop;
# ./configure --sbin-path=/usr/local/sbin --with-http_ssl_module
--without-mail_pop3_module --without-mail_imap_module
--without-mail_smtp_module --prefix=/var/lib/nginx
--sbin-path=/usr/sbin --conf-path=/etc/nginx/
--error-log-path=/var/log/nginx --http-log-path=/var/log/nginx
--pid-path=/var/run --lock-path=/var/lock
# make
# cd objs
# cp -f ./nginx /usr/sbin
# /etc/init.d/nginx start;

После этого уже должен быть запущен и обслуживать запросы свежий сервер nginx, который умеет все нужные нам штуки.

Документы из memcached

Когда nginx отдает напрямую файлы, он передает заголовок Content-type в соответствии с типом данного файла. Когда nginx проксирует apache, Content-type приходит от apache. Но когда nginx забирает документа из memcached, то Content-type не устанавливается. А значит, используется дефалтный. А дефалтный у нас по конфигу default_type application/octet-stream;, и это правильно. В этом случае при отдаче документа из кэша будет неправильно передаваться тип и некоторые браузеры предложат сохранить бинарный файл, вместо того чтобы открыть HTML-страницу. Чтобы ситуацию исправить, в случае отдачи из memcached устанавливаем заголовки (и, кстати, сжатие тоже) дополнительно:

set $memcached_key "habrometr$uri";
memcached_pass localhost:11211;
error_page 404 502 504 = @backend;
add_header Content-Type "text/html; charset=UTF-8";
gzip on;
gzip_proxied any;
gzip_types application/octet-stream;

При этом из memcached мы получаем только HTML в UTF-8.

Мухи отдельно, котлеты отдельно.

В качестве отдельной магии хотелось бы выделить сам способ выделения хаброметров по имени файла и обслуживания их по-особому. В секции location / выделяем эти файлы:

if (-f $document_root/image_cache${uri}) {
    rewrite ^.*$ /image_cache/$uri last;
    break;
}

Если файл находим в кэше, то просто возвращаем его пользователю, сообщая что файл можно закешировать до времени следующего обновления (модификация файла + 2 часа):

location /image_cache/ {
    root   /home/habrometr/public_html;
    expires modified +2h;
}

Обратите внимание на наличие флага last у rewrite и директивы break; за ней. Без использования этих двух директив мне не удалось заставить nginx 0.4 (на 0.7 я не проверял) сразу перейти в секцию location /image_cache/, т.е. после обнаружения файла он переходил к проскированию, что неверно.

]]> https://valera.ws/2009.01.26~nginx-magic/feed/ 2 Хабро́метр — новый сервис логирования и отображения значений кармы и хабросилы https://valera.ws/2009.01.14~habrometr/ https://valera.ws/2009.01.14~habrometr/#respond Wed, 14 Jan 2009 14:38:59 +0000 http://valera.ws/?p=269 Читать далее Хабро́метр — новый сервис логирования и отображения значений кармы и хабросилы ]]> Хаброметр — сервис логирования значений кармы, хабрасилы и позиции в рейтинге хабрапользователя и отображения этой информации на информерах, которые можно вставить в профиль, блог, форум и т.д.

Хаброметр feedbee Хаброметр feedbee

Хаброметр feedbee

Для этих целей уже давно был написан Кармаграф от Goodrone . Но развитие его сейчас заморожено, а сам движок сломался и значения счетчиков не обновляется с прошлого года (на момент написания). Это и подтолкнуло написать свой кармаграф именно сейчас.

Считать ли само слово «кармаграф» названием конкретного сервиса от Goodrone, либо же наименованием класса сервисов (или самого графика) — не знаю. Чтобы не заморачиваться с этим вопросом, свой кармаграф решил назвать иначе — Хаброметр. Ведь не только карму он считает и показывает, но еще хабрасилу и позицию в рейтинге (все, что выдает на данный момент API Хабра). Хаброметр отличается от Кармаграфа не смотря на визуальную схожесть дизайна основного информера. Конечно, я постарался взять все лучшее у Кармаграфа. Основное отличие Хаброметра — информеров будет несколько разных видов. Уже сейчас доступны три вида — кармаграфик, табло и минитабло.

Итак, пару слов о том что получилось. Весь сервис состоит из подсистемы сбора информации и подсистемы ее визуализации. Каждые 2 часа бот собирает информацию о значениях кармы, хабрасилы и позиции в рейтинге (я называю эти данные Хабразначениями) через API Хабра по всем зарегистрированным на сервисе пользователям. Данные сохраняются в базу. В любое время любой желающий может зайти на страницу пользователя и посмотреть историю его Хабразначений. Сейчас показывается история из 500 последних запросов (12 запросов в сутки, получается чуть больше 40 дней). В перспективе отображение определенно изменится, появится календарь. Кроме того, существуют графические информеры, на которых отображаются текущие Хабразначения юзера, максимальные и минимальные значения за время мониторинга и график кармы (наличие определенных компонентов зависит от типа (размера) информера).

Все скрипты написаны на PHP, СУБД используется MySQL (пока во всяком случае). Для рисования используется ImageMagick. Для запуска по расписанию — Cron.

Работает сервис на моем сервере (server.valera.ws), который, к слову, не очень мощный и может не выдержать хабраэффекта. Кстати, с технической точки зрения есть одно существенное отличие работы Хаброметра от Кармаграфа — информеры рисуются не по расписанию после скачки свежих данных, а при первом запросе на отображение информера после обновления данных. Другими словами, после прорисовки информера он кэшируется. Сама прорисовка происходит очень быстро. А кэш чистится после скачки свежих данных. Это позволяет разнести пиковую нагрузку на прорисовку свежих информеров во времени. К тому же, не все зарегистрированные юзеры вообще где-то разместят информеры, а тем более не разместят информеры сразу всех типов. Так что разовая прорисовка всех информеров была бы излишней.

Естественно сервис предоставляется «как есть» и бесплатный для использования. Код Хаброметра я собираюсь открыть, но несколько позже. Сначала требуется отладить его работу, исправить ошибки (которые там наверняка найдутся). Код будет открыт под лицензией GPL 3.

Сейчас сервис работает в режиме beta-тестирования. Буду рад вашим отзывам, багрепортам на e-mail feedbee@gmail.com. В первую очередь хочется добиться стабильности работы и оптимизации ресурсозатрат сервиса. Ну а далее уже заботиться об удобстве пользования.

Первым делом постараюсь доделать запланированные виды информеров. Это 31х31, 88х31 и 350х20. Так же надо будет что-то придумать с расширением цветовых гамм. Когда будет время, поработаю над дизайном страниц сервиса.

P.S. Сайт сервиса (описание системы и бота): http://habrometr.server.valera.ws.

P.S.S. Домен такой выбирал не специально, так случилось что server.valera.ws != valera.ws. Потому такой длинный.

P.S.S. Если из-за хабраэффекта накроется мой сервер, уже не кидайте много камней в огород. Пока не было возможности испытать его под нагрузкой.

]]>
https://valera.ws/2009.01.14~habrometr/feed/ 0