Привет, хабр!
Задача заключается в создании распределенного хранилища изображений, которые необходимо хранить в различных размерах, исходные картинки могут храниться на любом из серверов.
Мы пришли к следующей схеме: есть физическая машина, являющаяся точкой входа, и N дополнительных хранилищ. На главном сервере установлены nginx, слушающий 80 порт и apache. При обращении к серверу nginx ищет обработанное изображение в файловой системе, если не находит, пытается найти изображение на дополнительных серверах. Если изображения нет и там, через apache nginx обращается к PHP скрипту, который ищет исходник нужного изображения на машинах (загружатся изображение может напрямую на любую из машин), выполняет необходимые обработки и сохраняет обработанные изображения в хранилище. Если же не найден оригинал изображения, php отдает 404-е заголовки, в ответ на которые nginx отдает картинку-заглушку.
Прошу советов по самой схеме работы (возможно, есть альтернативные варианты построения взаимодействия, которые нам подойдут?), а так же помощи с конфигурированием nginx (конкретно — не получается обработать возвращаемые apache заголовки в nginx через error_page).
Не совсем понятно, почему «исходные картинки могут храниться на любом из серверов».
Логично было бы сделать такую архитектуру, при которой по имени изображения можно однозначно определить ее местоположение.
1. В момент загрузки картинки определять сервер, на котором она должна храниться.
2. Именовать картинку соответственно хранилищу.
3. При отдаче вы точно знаете, где ее искать.
Имхо будет совсем не быстро. Ну локально файл проверить не долго, а вот проверить на других N серверах будет уже и не быстро. И опять же узким местом может оказаться канал до балансировщика (точки входа).
Я бы посоветовал рассмотреть вариант, когда на этапе генерации html контента уже известно месторасположение картинок. Каждый сервер имеет своё поддомен, например: img1.domain.com, img2.domain.com и т.д. Здесь вы так же сможете легко накинуть dns балансировку.
Ну и соответственно, если нет готовой картинки, уже отдаёте путь до пхп скрипта.
Хранить id хранилища рядом с нужной версией картинки будет не сложно.
К главной машине через NFS подключены N с превюшками.
Через NFS легко проверяем на каком из серверов есть уже обработанное изображение и редиректим на него.
Изначально через NFS планировалось только проверять наличие файла, но после долгих тестов оказалось что даже с записью превюшек на другие сервера он справляется на ура. Падает не чаще раз в год.
А почему не сделать простую БД на точке входа, в которой будет храниться путь до картинки (или хотябы имя сервера)? Поиск все равно будет производиться, но на правильно сделаной базе искать придется намного меньше, и работать будет намного быстрее.
С БД нагрузка вырастает в разы. Коннект к БД+скриптовый язык… При большом количестве запросов это убийственно. И оптимизация БД тут играет очень малую роль.
Идея:
0 опционально — поиск на локальной ФС
1 создаётся хэш-функция от пути к картинке, возвращающая целое число. hashVal
2. выбирается фронт сервер с номером hashVal % serversCount, и если он живой — стягивается картинка с этого сервера. если её нет — то генерится.
3. если сервер не живой — берем следующий.и переходим к пункту 2.
На практике такое решение ущербно по причине того, что после выхода из строя сервера — следующий сервер становится перегружен.
В живом проэкте используется модификация:
Есть 1000 записей в мэмкеше. Инициированых значениями из пункта 2. После падения фронт сервера его индексы меняются на индексы живых серверов случайным образом. После поднятия — восстанавливаются.
(на самом деле чуть сложнее, так как каждая картинка живет всегда на 3х серверах, и доступ к ним балансируется каруселью, но это не важно для этого вопроса)