我正在调查Jetty 6.1.26的性能问题。Jetty似乎使用了Transfer-Encoding: chunked
,并且根据所使用的缓冲区大小,这种传输方式在本地传输时可能非常缓慢。
我创建了一个小的Jetty测试应用程序,其中包含一个演示此问题的单个servlet。
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();
}
}
在我的实验中,我使用一个大小为128MB的测试文件,在本地主机上使用客户端连接,由servlet返回。使用Java编写的简单测试客户端(使用URLConnection
)下载该数据需要3.8秒,这非常慢(是的,它的速度是33MB / s,听起来不慢,但这是纯粹的本地和输入文件已缓存; 它应该更快)。现在问题出现了。如果我使用HTTP / 1.0客户端wget下载数据,因此不支持分块传输编码,则只需0.1秒即可完成。 这是一个更好的数字。
现在,当我将
bufferSize
更改为4096时,Java客户端需要0.3秒。如果我完全删除对
resp.setBufferSize
的调用(似乎使用24KB块大小),那么Java客户端现在需要7.1秒,并且wget突然也变得同样慢!请注意,我绝不是Jetty的专家。在诊断Hadoop 0.20.203.0中reduce任务洗牌的性能问题时,我偶然发现了这个问题。Hadoop在Jetty中以与简化版代码类似的方式传输文件,使用64KB缓冲区大小。
该问题在我们的Linux(Debian)服务器和我的Windows机器上都会出现,并且在Java 1.6和1.7上均表现出来,因此似乎仅依赖于Jetty。
有人知道是什么原因引起的吗?是否有什么我可以做的?