В этом посте я расскажу, как правильно настроить Nginx для максимально эффективной отдачи больших файлов из вашего rails приложения. К своему удивлению, на русском языке я такой информации не нашел. Есть статьи про использование X-Accel-Redirect совместно с php, а вот чтобы про рельсы да на родном языке что-то не густо… Вобщем, постараюсь восполнить этот пробел.
Итак, исходные данные следующие:
Как бы мне не хотелось избежать ссылок на англоязычные источники, но пройти мимо официальной документации я не могу. В Nginx есть замечательная штука X-Accel-Redirect, с помощью которой и решается наша задача. Сейчас я покажу, как заставить ее работать.
В конфигах nginx нужно добавить:
Все. Настройка закончена. Давайте посмотрим, как все это работает на примере скачивания файла. На этом этапе считаем что Nginx и Rails приложение уже настроены и работают.
2. Nginx получает этот запрос и передает его rails приложению, добавив еще один заголовок.
3. Контроллер rails приложения проверяет, можно ли отдать файл этому пользователю, и вызывает send_file. В этом месте важно использовать абсолютный путь.
4. Rails (точнее Rack) решает, что делать с этим файлом. Раньше на этом этапе содержимое файла включалось в тело ответа. Но мы поменяли настройки Rails приложения, и теперь вместо содержимого файла в ответе будет заголовок X-Accel-Redirect, который потом обработает Nginx. Тело ответа будет пустым. Также, меняется путь к файлу в соответствии с информацией из заголовка X-Accel-Mapping.
5. Nginx обрабатывает этот заголовок, находит подходящий location, меняет путь к файлу и отдает его пользователю.
6. Браузер пользователя получает файл.
Задача
Итак, исходные данные следующие:
- мы используем Ruby on Rails, запросы пользователей проходят через Nginx;
- нам нужно отдавать большие файлы;
- файлы должны быть недоступны для прямого скачивания, решение об отдаче файла принимает rails приложение.
Решение
Как бы мне не хотелось избежать ссылок на англоязычные источники, но пройти мимо официальной документации я не могу. В Nginx есть замечательная штука X-Accel-Redirect, с помощью которой и решается наша задача. Сейчас я покажу, как заставить ее работать.
В нашем Rails приложении в файле config/environments/production.rb нужно раскоментировать строчку:
config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
В конфигах nginx нужно добавить:
proxy_set_header X-Accel-Mapping /mnt/filestorage/=/private_files/;
location /private_files/ {
internal;
alias /mnt/filestorage/;
}
Все. Настройка закончена. Давайте посмотрим, как все это работает на примере скачивания файла. На этом этапе считаем что Nginx и Rails приложение уже настроены и работают.
1. Браузер запрашивает файл
# HTTP заголовок
GET /download/kino.avi HTTP/1.1
2. Nginx получает этот запрос и передает его rails приложению, добавив еще один заголовок.
# конфиг Nginx
proxy_set_header X-Accel-Mapping /mnt/filestorage/=/private_files/;# HTTP запрос
GET /download/kino.avi HTTP/1.1
X-Accel-Mapping: /mnt/filestorage/=/private_files/
3. Контроллер rails приложения проверяет, можно ли отдать файл этому пользователю, и вызывает send_file. В этом месте важно использовать абсолютный путь.
# контроллер rails приложения (например app/controllers/downloads_controller.rb)
send_file('/mnt/filestorage/kino.avi')
4. Rails (точнее Rack) решает, что делать с этим файлом. Раньше на этом этапе содержимое файла включалось в тело ответа. Но мы поменяли настройки Rails приложения, и теперь вместо содержимого файла в ответе будет заголовок X-Accel-Redirect, который потом обработает Nginx. Тело ответа будет пустым. Также, меняется путь к файлу в соответствии с информацией из заголовка X-Accel-Mapping.
# конфигурация Rails (config/environments/production.rb)
config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'# HTTP ответ
HTTP/1.1 200 OK
X-Accel-Redirect: /private_files/kino.avi
Content-Type: application/octet-stream
Content-length: ...
Content-Disposition: attachment; filename="kino.avi"
пустое тело ответа
5. Nginx обрабатывает этот заголовок, находит подходящий location, меняет путь к файлу и отдает его пользователю.
# конфиг Nginx
location /private_files/ {
internal;
alias /mnt/filestorage/;
}# HTTP ответ
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Length: ...
Content-Disposition: attachment; filename="kino.avi"
<содержимое /mnt/filestorage/kino.avi>
6. Браузер пользователя получает файл.
Если вы используете Passenger, то в конфигах Nginx'а нужно вместо proxy_set_header использовать passenger_set_cgi_param
This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers.
Комментариев нет:
Отправить комментарий