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 中的传输速度较慢,在特定缓冲区大小下采用分块传输编码 的相关文章

  • 如何使用 FileChannel 将一个文件的内容附加到另一个文件的末尾?

    File a txt好像 ABC File d txt好像 DEF 我正在尝试将 DEF 附加到 ABC 所以a txt好像 ABC DEF 我尝试过的方法总是完全覆盖第一个条目 所以我总是最终得到 DEF 这是我尝试过的两种方法 File
  • 如何测试 JUnit 测试的 Comparator?

    我需要测试 Compare 方法 但我对如何测试感到困惑 我可以看看该怎么做吗 public class MemberComparator implements Comparator
  • 线程自动利用多个CPU核心?

    假设我的应用程序运行 2 个线程 例如渲染线程和游戏更新线程 如果它在具有多核 CPU 当今典型 的移动设备上运行 我是否可以期望线程在可能的情况下自动分配给不同的核心 我知道底层操作系统内核 Android linux内核 决定调度 我的
  • ExceptionConverter:java.io.IOException:文档没有页面。我正在使用 iText

    当我执行下面的代码时 File f new File c sample pdf PdfWriter getInstance document new FileOutputStream f document open System out p
  • Convert.FromBase64String 方法的 Java 等效项

    Java 中是否有相当于Convert FromBase64String http msdn microsoft com en us library system convert frombase64string aspx which 将指
  • java中删除字符串中的特殊字符?

    如何删除字符串中除 之外的特殊字符 现在我用 replaceAll w s 它删除了所有特殊字符 但我想保留 谁能告诉我我该怎么办 Use replaceAll w s 我所做的是将下划线和连字符添加到正则表达式中 我添加了一个 连字符之前
  • hibernate总是自己删除表中的所有数据

    您好 我正在开发一个 spring mvc 应用程序 它使用 hibernate 连接到存储文件的 mysql 数据库 我有两个方法 一个方法添加我选择的特定文件路径中的所有文件 另一种方法调用查询以返回从 mysql 存储的文件列表 问题
  • OnClick 事件中的 finish() 如何工作?

    我有一个Activity一键退出Activity 通过layout xml我必须设置OnClick事件至cmd exit调用 this finish 效果很好 public void cmd exit View editLayout thi
  • 迁移到 java 17 后有关“每个进程的内存映射”和 JVM 崩溃的 GC 警告

    我们正在将 java 8 应用程序迁移到 java 17 并将 GC 从G1GC to ZGC 我们的应用程序作为容器运行 这两个基础映像之间的唯一区别是 java 的版本 例如对于 java 17 版本 FROM ubuntu 20 04
  • 序列化对象以进行单元测试

    假设在单元测试中我需要一个对象 其中所有 50 个字段都设置了一些值 我不想手动设置所有这些字段 因为这需要时间而且很烦人 不知何故 我需要获得一个实例 其中所有字段都由一些非空值初始化 我有一个想法 如果我要调试一些代码 在某个时候我会得
  • 使用 Flyway 和 Hibernate 的 hbm2ddl 在应用程序的生命周期中管理数据库模式

    我正在开发 Spring Hibernate MySql 应用程序 该应用程序尚未投入生产 我目前使用 Hibernatehbm2ddl该功能对于管理域上的更改非常方便 我也打算用Flyway用于数据库迁移 在未来的某个时候 该应用程序将首
  • Tomcat 6找不到mysql驱动

    这里有一个类似的问题 但关于类路径 ClassNotFoundException com mysql jdbc Driver https stackoverflow com questions 1585811 classnotfoundex
  • 当单元格内的 JComboBox 中有 ItemEvent 时,如何获取 CellRow

    我有一个 JTable 其中有一列包含 JComboBox 我有一个附加到 JComboBox 的 ItemListener 它会根据任何更改进行操作 但是 ItemListener 没有获取更改的 ComboBox 所在行的方法 当组合框
  • Windows 上的 Nifi 命令

    在我当前的项目中 我一直在Windows操作系统上使用apache nifi 我已经提取了nifi 0 7 0 bin zip文件输入C 现在 当我跑步时 bin run nifi bat as 管理员我在命令行上看到以下消息 但无法运行
  • Keycloak - 自定义 SPI 未出现在列表中

    我为我的 keycloak 服务器制作了一个自定义 SPI 现在我必须在管理控制台上配置它 我将 SPI 添加为模块 并手动安装 因此我将其放在 module package name main 中 并包含 module xml 我还将其放
  • Java - 不要用 bufferedwriter 覆盖

    我有一个程序可以将人员添加到数组列表中 我想做的是将这些人也添加到文本文件中 但程序会覆盖第一行 因此这些人会被删除 如何告诉编译器在下一个空闲行写入 import java io import java util import javax
  • 休眠以持久保存日期

    有没有办法告诉 Hibernate java util Date 应该持久保存 我需要这个来解决 MySQL 中缺少的毫秒分辨率问题 您能想到这种方法有什么缺点吗 您可以自己创建字段long 或者使用自定义的UserType 实施后User
  • 中断连接套接字

    我有一个 GUI 其中包含要连接的服务器列表 如果用户单击服务器 则会连接到该服务器 如果用户单击第二个服务器 它将断开第一个服务器的连接并连接到第二个服务器 每个新连接都在一个新线程中运行 以便程序可以执行其他任务 但是 如果用户在第一个
  • JAVA - 如何从扫描仪读取文件中检测到“\n”字符

    第一次海报 我在读取文本文件的扫描仪中读取返回字符时遇到问题 正在读取的文本文件如下所示 test txt start 2 0 30 30 1 1 90 30 0 test txt end 第一行 2 表示两个点 第二行 位置索引 0 xp
  • Swagger/Openapi-Annotations:如何使用 $ref 生成 allOf?

    我正在生成 Rest 端点 包括添加OpenAPI Swagger对生成的代码进行注释 虽然它对于基本类型运行得很好 但我在自定义类方面遇到了一些问题 现在我有很多自定义类的重复架构条目 使用 Schema 实现 MyClass class

随机推荐