Nginx
June 2010 4

Генератор миниатюрок из Nginx-а

Итак, сегодня мы соберём генератор миниатюрок на базе любимого народом веб-сервера — nginx-а. Что примечательно, сделаем мы это без единого гвоздя, т.е. без единой строчки кода, не считая конфигурации.

Старая школа велит нам генерировать все необходимые миниатюрки сразу после загрузки базового изображения. Способ проверенный и надёжный, однако, мы живём в изменчивом новом мире, в котором нам может понадобится новый размер миниатюрок с утра в понедельник. Поэтому генерировать миниатюрки будем на лету, по запросу.

Для дела нам понадобится ngx_http_image_filter_module, модуль, который умеет трансформировать изображения в форматах JPEG, GIF и PNG. Модуль стандартный, но по-умолчанию не собирается, поэтому перед сборкой нгинкса нужно добавить его:
./configure --with-http_image_filter_module
Теперь ставим libgd способом, принятым у нас в системе, и собираем нгинкс. Кстати, при сборке из портов FreeBSD достаточно выполнить make config и выбрать нужный модуль в списке.

Просто пережимка
Для простоты будем считать, что у нас есть домен images.domain.ru, с которого выдаются картинки лежащие в /path/to/images. Запросы к /some/image.jpg будут выдаваться напрямую, к /120x90/some/image.jpg или /120x-/some/image.jpg будут жаться в миниатюрки (во втором случае идёт масштабирование только по ширине). В таком случае получаем:
server {
    server_name images.domain.ru;
    root /path/to/images;

    if ($uri ~ ^/(\d+|-)x(\d+|-)/) {
        set $w $1;
        set $h $2;
    }

    location / {
    }

    location ~ ^/(?:\d+|-)x(?:\d+|-)/.*\.(?:jpg|gif|png)$ {
        rewrite ^/[\w\d-]+/(.*)$ /$1;
        image_filter resize $w $h;
        break;
    }
}

Что тут происходит? Сначала мы определяем размеры миниатюрки и устанавливаем соответствующие переменные. Затем выдаём статику и переходим к выдаче миниатюрок, рассмотрим второй location по-подробнее: rewrite перенаправляет url на настоящий файл, image_filter уменьшает полученную картинку, break предотвращает внутренний редирект с выходом из location. Последнее важно иначе нгинкс пройдёт весь путь ещё раз с уже переписанным URL и выдаст полную картинку.

Можно добавить предопределённые размеры:
if ($uri ~ ^/small/) {
    set $w 120;
    set $h 90;
}

location ~ ^/(?:\d+|-)x(?:\d+|-)|small/.*\.(?:jpg|gif|png)$ {
    ...

Этим можно уже пользоваться если не жалко процессора, к слову на FreeBSD 8/Core 2 Quad Q9550 @ 2.83GHz/DDR2-800 удавалось пережимать 200 картинок в секунду (jpeg, 640x480 -> 150x150). Но, конечно, нам процессор жалко, поэтому будем пережатые картинки кэшировать.

Пережимка с кэшированием
Кэшированием в нгинксе занимается ngx_http_proxy_module, кэширует он только то, что проксирует, поэтому нам понадобиться извернутся — будем слушать два порта и проксировать сами себя:
proxy_cache_path /path/to/cache levels=1:2 keys_zone=thumbs:10m inactive=24h max_size=5G;

server {
    server_name images.domain.ru;

    location ~ ^/(?:\d+|-)x(?:\d+|-)|small/ {
        proxy_pass http://localhost:8081;
        proxy_cache thumbs;
        proxy_cache_valid  200      24h;
        proxy_cache_valid  404 415  1m;
    }

    location / {
        root /path/to/images;
    }
}

server {
    listen 8081;

    ... конфигурация пережималки ...
}

Готово, теперь на вышеупомянутой системе нгинкс выдаёт 15000 картинок в секунду, если всё из кэша, — чертовски быстро. Я не буду расписывать настройки, они неплохо расписаны в документации модуля. А полный конфиг можно взять тут.
+71
20.1k 254
Comments 42
Top of the day