Sendfile on Tomcat, Apache, Nginx
Рубрика: Development, Java | 4 September 2008, 14:53 |
Vadim Voituk
Наверное все, кто хоть когда-либо использовал web-сервер nginx, знают о такой его возможности как “X-Accel-Redirect”.
Для тех кто не знает / не помнит – напомню.
Суть состоит в том, что при необходимости отдать клиенту файл посредством своего скрипта, можно не писать код, который будет читать байтики из файла и писать их в сокет, а отправить специальный response header, получив который nginx сам начнет отправку файла.
Например на PHP это выглядит так:
header("X-Accel-Redirect: /protected/iso.img");
Такой подход существенно повышает производительность приложений, сохраняя ресурсы сервера, и конечно же экономит много нервных клеток разработчикам, которым не прийдется реализовывать отдачу файла по HTTP, поддержку докачки и тд. и тп.
Собсно как подобное реализуется на nginx – написано парой строчек выше.
Если что не очень ясно – Google в помощь.
Не так давно узнал даже как реализовать описанное и в старом-добром Apache.
Для этого нужно установить дополнительный модуль mod_xsendfile, активировать, ну а дальше все аналогично nginx, только c отличным именем response header:
X-Sendfile: /path/to/file
Есть только одно НО в этом способе – я его ещё не пробовал :)
Если кто имеет опыт работы с этим модулем – дайте знать, хочется услышать отзывы и мнения :)
А недавно пришлось реализовать отдачу достаточно тяжеловесного контента на Tomcat, и захотелось чего-то подобного.
Путем недолгого гугления было найдено такое решение:
В первую очередь необходимо активировать NIO connector в настройках сервера
(документация говорит что можно и APR, но я не пробовал)
Для этого в conf/server.xml вместо стандартного
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000"
redirectPort="8443" />
Пишем
<Connector port="8180" protocol="org.apache.coyote.http11.Http11NioProtocol"
redirectPort="8443" />
После этого проверяем поддержку sendfile наличием аттрибута
org.apache.tomcat.sendfile.support в request-е сервлета
Если значение установлено в false – то в Connector-е дописываем useSendfile="true"
Код отправки файла клиенту выглядит так:
if (request.getAttribute("org.apache.tomcat.sendfile.support")==Boolean.TRUE){
File f = new File(cont.getRealPath("/3499.3gp"));
long start = 0L;
long end = f.length();
request.setAttribute("org.apache.tomcat.sendfile.filename", f.getCanonicalPath());
request.setAttribute("org.apache.tomcat.sendfile.start", start);
request.setAttribute("org.apache.tomcat.sendfile.end", end);
request.setAttribute("org.apache.tomcat.sendfile.token", this);
response.setContentLength(new Long(end).intValue());
}
Немного отличается от того, как это выглядит в nginx и apache, но ничто не мешает вынести эту логику в фильтр, который будет срабатывать при наличии соответствующего HTTP заголовка в response-e
Tweet
Lighttpd тоже поддерживает X-Sendfile.
Я не понял, зачем такие сложности? Почему нельзя просто написать:
response.addHeader(“X-Sendfile”, “/path/to/file”);
Степан,
Про lighttpd не знаю – никогда с ним не стыкался.
Просто написать
response.addHeader("X-Sendfile", "/path/to/file");прокатит только если у тебя Tomcat спрятан за каким-нить nginx, который этот X-Sendfile поймет и выполнит отдачу.
В случае же если приходится отдавать файл Tomcat-ом – то нужно делать как описано выше :)
А, я понял, сам Tomcat поддерживает sendfile. Как интересно.
Ага, именно так.
Причем если нужно отдать только часть файла, то это можно регулировать аттрибутами
org.apache.tomcat.sendfile.start
org.apache.tomcat.sendfile.end
Ещё не пробовал запустить, но конструкция (new Long(end).intValue() мне изначально не нравится — файлы больше 2Гб по сети не передаются, чтоль?
DieZ,
Хороший вопрос, но скорее всего
уже к разработчикам ServletAPI
Ставьте длину не ч-з setContentLength хелпер, а ч-з addHeader(“Content-Length” и будет вам лонговое счастье :)
@akabanov
ХитEр :)
Кто нибудь ограничение ширины канала при скачивании делал при помощи tomcat + sendfile ?
@filinberg – IMHO не томкатовое это дело – лучше доверить это nginx или apache, что стоят на фронтенде.
согласен, но это для статических файлов, параллельно надо решить задачу и мониторинга трафика
Помогло вытаскивание org.apache.tomcat.util.net.NioChannel при помощи рефлексии из HttpServletResponse, все досталось и даже работает, теперь напрямую в сервлете через sendfile посылаю файл кусками.
SendfileData sendfileData = new NioEndpoint.SendfileData();
sendfileData.fileName = file.getCanonicalPath();
sendfileData.pos = 0L;
sendfileData.length = file.length();
// Затем как в исходниках Http11NioProcessor (Http11ArpProcessor)
KeyAttachment ka = (KeyAttachment) socket.getAttachment(false);
ka.setSendfileData(sendfileData);
sendfileData.keepAlive = true;
SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());
boolean openSocket = socket.getPoller().processSendfile(key, ka, true);
Оформил этот кусок как опцию (т.е. если доступен sendfile то задействуем), вполне так решение …