Jetty 中的传输速度较慢,在特定缓冲区大小下采用分块传输编码

2024-03-08

我正在调查 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();
    }

}

在我的实验中,我使用 servlet 返回给客户端的 128MB 测试文件,客户端使用 localhost 进行连接。使用用 Java 编写的简单测试客户端下载此数据(使用URLConnection)需要 3.8 秒,这非常慢(是的,它是 33MB/s,这听起来并不慢,除了这纯粹是本地的并且输入文件被缓存;它应该快得多)。

现在事情变得奇怪了。如果我使用 wget 下载数据,它是一个 HTTP/1.0 客户端,因此不支持分块传输编码,只需要 0.1 秒。这是一个更好的数字。

现在当我改变时bufferSize到4096,Java客户端需要0.3秒。

如果我删除对resp.setBufferSize完全(似乎使用 24KB 块大小),Java 客户端现在需要 7.1 秒,并且 wget 突然同样慢!

请注意,我绝不是 Jetty 方面的专家。我在使用reduce任务洗牌诊断Hadoop 0.20.203.0中的性能问题时偶然发现了这个问题,它使用Jetty以类似于缩减示例代码的方式传输文件,缓冲区大小为64KB。

该问题在我们的 Linux (Debian) 服务器和我的 Windows 机器上以及 Java 1.6 和 1.7 上都会重现,因此它似乎完全依赖于 Jetty。

有谁知道可能导致此问题的原因是什么,以及我是否可以对此做些什么?


我相信通过查看 Jetty 源代码,我自己已经找到了答案。它实际上是响应缓冲区大小、传递给的缓冲区大小的复杂相互作用outStream.write,以及是否outStream.flush被称为(在某些情况下)。问题在于 Jetty 使用其内部响应缓冲区的方式,以及如何将写入输出的数据复制到该缓冲区,以及何时以及如何刷新该缓冲区。

如果使用的缓冲区大小outStream.write等于响应缓冲区(我认为倍数也可以),或者更少并且outStream.flush使用的话,性能还是不错的。每个write然后调用直接刷新到输出,这很好。但是,当写入缓冲区较大且不是响应缓冲区的倍数时,这似乎会导致处理刷新的方式出现一些奇怪的情况,从而导致额外的刷新,从而导致性能不佳。

在分块传输编码的情况下,电缆中存在额外的扭结。对于除第一个块之外的所有块,Jetty 保留 12 个字节的响应缓冲区来包含块大小。这意味着,在我最初使用 64KB 写入和响应缓冲区的示例中,响应缓冲区中容纳的实际数据量仅为 65524 字节,因此,部分写入缓冲区再次溢出到多次刷新中。查看此场景捕获的网络跟踪,我发现第一个块是 64KB,但所有后续块都是 65524 字节。在这种情况下,outStream.flush没有什么区别。

当使用 4KB 缓冲区时,我仅在以下情况下才看到快速速度:outStream.flush被称为。事实证明resp.setBufferSize只会增加缓冲区大小,并且由于默认大小是 24KB,resp.setBufferSize(4096)是一个空操作。然而,我现在正在写入 4KB 数据,即使保留了 12 个字节,这些数据也适合 24KB 缓冲区,然后由outStream.flush称呼。然而,当调用flush被删除,它将让缓冲区填满,再次有 12 个字节溢出到下一个块中,因为 24 是 4 的倍数。

综上所述

看来要获得 Jetty 的良好性能,您必须:

  • 打电话时setContentLength(无分块传输编码)并使用缓冲区write它的大小与响应缓冲区的大小相同。
  • 当使用分块传输编码时,使用比响应缓冲区大小至少小 12 个字节的写入缓冲区,并调用flush每次写完后。

请注意,“慢”场景的性能仍然如此,您可能只会在本地主机或非常快(1Gbps 或更高)的网络连接上看到差异。

我想我应该为此针对 Hadoop 和/或 Jetty 提交问题报告。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Jetty 中的传输速度较慢,在特定缓冲区大小下采用分块传输编码 的相关文章

  • Java中反射是如何实现的?

    Java 7 语言规范很早就指出 本规范没有详细描述反射 我只是想知道 反射在Java中是如何实现的 我不是问它是如何使用的 我知道可能没有我正在寻找的具体答案 但任何信息将不胜感激 我在 Stackoverflow 上发现了这个 关于 C
  • 在 HTTPResponse Android 中跟踪重定向

    我需要遵循 HTTPost 给我的重定向 当我发出 HTTP post 并尝试读取响应时 我得到重定向页面 html 我怎样才能解决这个问题 代码 public void parseDoc final HttpParams params n
  • 无法展开 RemoteViews - 错误通知

    最近 我收到越来越多的用户收到 RemoteServiceException 错误的报告 我每次给出的堆栈跟踪如下 android app RemoteServiceException Bad notification posted fro
  • 加速代码 - 3D 数组

    我正在尝试提高我编写的一些代码的速度 我想知道从 3d 整数数组访问数据的效率如何 我有一个数组 int cube new int 10 10 10 我用价值观填充其中 然后我访问这些值数千次 我想知道 由于理论上所有 3d 数组都存储在内
  • 反射找不到对象子类型

    我试图通过使用反射来获取包中的所有类 当我使用具体类的代码 本例中为 A 时 它可以工作并打印子类信息 B 扩展 A 因此它打印 B 信息 但是当我将它与对象类一起使用时 它不起作用 我该如何修复它 这段代码的工作原理 Reflection
  • JavaMail 只获取新邮件

    我想知道是否有一种方法可以在javamail中只获取新消息 例如 在初始加载时 获取收件箱中的所有消息并存储它们 然后 每当应用程序再次加载时 仅获取新消息 而不是再次重新加载它们 javamail 可以做到这一点吗 它是如何工作的 一些背
  • 操作错误不会显示在 JSP 上

    我尝试在 Action 类中添加操作错误并将其打印在 JSP 页面上 当发生异常时 它将进入 catch 块并在控制台中打印 插入异常时出错 请联系管理员 在 catch 块中 我添加了它addActionError 我尝试在jsp页面中打
  • 无法解析插件 Java Spring

    我正在使用 IntelliJ IDEA 并且我尝试通过 maven 安装依赖项 但它给了我这些错误 Cannot resolve plugin org apache maven plugins maven clean plugin 3 0
  • Java TestNG 与跨多个测试的数据驱动测试

    我正在电子商务平台中测试一系列商店 每个商店都有一系列属性 我正在考虑对其进行自动化测试 是否有可能有一个数据提供者在整个测试套件中提供数据 而不仅仅是 TestNG 中的测试 我尝试不使用 testNG xml 文件作为机制 因为这些属性
  • 加密 JBoss 配置中的敏感信息

    JBoss 中的标准数据源配置要求数据库用户的用户名和密码位于 xxx ds xml 文件中 如果我将数据源定义为 c3p0 mbean 我会遇到同样的问题 是否有标准方法来加密用户和密码 保存密钥的好地方是什么 这当然也与 tomcat
  • Java Integer CompareTo() - 为什么使用比较与减法?

    我发现java lang Integer实施compareTo方法如下 public int compareTo Integer anotherInteger int thisVal this value int anotherVal an
  • 如何在控制器、服务和存储库模式中使用 DTO

    我正在遵循控制器 服务和存储库模式 我只是想知道 DTO 在哪里出现 控制器应该只接收 DTO 吗 我的理解是您不希望外界了解底层域模型 从领域模型到 DTO 的转换应该发生在控制器层还是服务层 在今天使用 Spring MVC 和交互式
  • Java执行器服务线程池[关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 如果我使用 Executor 框架在
  • 如何从指定日期获取上周五的日期? [复制]

    这个问题在这里已经有答案了 如何找出上一个 上一个 星期五 或指定日期的任何其他日期的日期 public getDateOnDay Date date String dayName 我不会给出答案 先自己尝试一下 但是 也许这些提示可以帮助
  • Java列表的线程安全

    我有一个列表 它将在线程安全上下文或非线程安全上下文中使用 究竟会是哪一个 无法提前确定 在这种特殊情况下 每当列表进入非线程安全上下文时 我都会使用它来包装它 Collections synchronizedList 但如果不进入非线程安
  • 如何在桌面浏览器上使用 webdriver 移动网络

    我正在使用 selenium webdriver 进行 AUT 被测应用程序 的功能测试自动化 AUT 是响应式网络 我几乎完成了桌面浏览器的不同测试用例 现在 相同的测试用例也适用于移动浏览器 因为可以从移动浏览器访问 AUT 由于它是响
  • 在 Maven 依赖项中指定 jar 和 test-jar 类型

    我有一个名为 commons 的项目 其中包含运行时和测试的常见内容 在主项目中 我添加了公共资源的依赖项
  • 捕获的图像分辨率太大

    我在做什么 我允许用户捕获图像 将其存储到 SD 卡中并上传到服务器 但捕获图像的分辨率为宽度 4608 像素和高度 2592 像素 现在我想要什么 如何在不影响质量的情况下获得小分辨率图像 例如我可以获取或设置捕获的图像分辨率为原始图像分
  • 节拍匹配算法

    我最近开始尝试创建一个移动应用程序 iOS Android 它将自动击败比赛 http en wikipedia org wiki Beatmatching http en wikipedia org wiki Beatmatching 两
  • Spring Boot @ConfigurationProperties 不从环境中检索属性

    我正在使用 Spring Boot 1 2 1 并尝试创建一个 ConfigurationProperties带有验证的bean 如下所示 package com sampleapp import java net URL import j

随机推荐