Медленные передачи в Jetty с кодировкой передачи chunked при определенном размере буфера

Я изучаю проблему производительности в Jetty 6.1.26. Jetty использует Transfer-Encoding: chunked, и в зависимости от размера используемого буфера это может быть очень медленным при локальной передаче.

Я создал небольшое тестовое приложение Jetty с одним сервлетом, которое демонстрирует проблему.

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.mortbay.jetty.Server;
import org.mortbay.jetty.nio.SelectChannelConnector;
import org.mortbay.jetty.servlet.Context;

public class TestServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        final int bufferSize = 65536;
        resp.setBufferSize(bufferSize);
        OutputStream outStream = resp.getOutputStream();

        FileInputStream stream = null;
        try {
            stream = new FileInputStream(new File("test.data"));
            int bytesRead;
            byte[] buffer = new byte[bufferSize];
            while( (bytesRead = stream.read(buffer, 0, bufferSize)) > 0 ) {
                outStream.write(buffer, 0, bytesRead);
                outStream.flush();
            }
        } finally   {
            if( stream != null )
                stream.close();
            outStream.close();
        }
    }

    public static void main(String[] args) throws Exception {
        Server server = new Server();
        SelectChannelConnector ret = new SelectChannelConnector();
        ret.setLowResourceMaxIdleTime(10000);
        ret.setAcceptQueueSize(128);
        ret.setResolveNames(false);
        ret.setUseDirectBuffers(false);
        ret.setHost("0.0.0.0");
        ret.setPort(8080);
        server.addConnector(ret);
        Context context = new Context();
        context.setDisplayName("WebAppsContext");
        context.setContextPath("/");
        server.addHandler(context);
        context.addServlet(TestServlet.class, "/test");
        server.start();
    }

}

В своем эксперименте я использую тестовый файл размером 128 МБ, который сервлет возвращает клиенту, подключающемуся через localhost. Загрузка этих данных с помощью простого тестового клиента, написанного на Java (с использованием URLConnection), занимает 3,8 секунды, что очень медленно (да, это 33 Мб/с, что не кажется медленным, за исключением того, что это чисто локальный процесс и входной файл был кэширован; он должен быть намного быстрее).

Теперь вот что становится странным. Если я загружу данные с помощью wget, который является клиентом HTTP/1.0 и поэтому не поддерживает кодировку chunked transfer, это займет всего 0,1 секунды. Это гораздо лучше.

Теперь, когда я изменяю bufferSize на 4096, Java-клиенту требуется 0,3 секунды.

Если я полностью удалю вызов resp.setBufferSize (который, похоже, использует размер чанка 24 КБ), то Java-клиент теперь работает 7,1 секунды, а wget внезапно стал таким же медленным!

Пожалуйста, обратите внимание, что я ни в коей мере не являюсь экспертом в Jetty. Я наткнулся на эту проблему во время диагностики проблемы производительности в Hadoop 0.20.203.0 с перестановкой задач reduce, который передает файлы с помощью Jetty способом, очень похожим на сокращенный пример кода, с размером буфера 64 КБ.

Проблема воспроизводится как на наших серверах Linux (Debian), так и на моей машине Windows, и как с Java 1.6, так и с 1.7, так что, похоже, она зависит только от Jetty.

Есть ли у кого-нибудь идеи, что может быть причиной этого, и могу ли я что-нибудь с этим сделать?

17
задан Sven 31 January 2012 в 04:41
поделиться