高并发 Apache 异步 HTTP 客户端 IOReactor 问题

2024-01-18

应用说明:

  • 我正在使用由 Comsat 的 Quasar FiberHttpClient(版本 0.7.0)包装的 Apache HTTP 异步客户端(版本 4.1.1)来运行和执行高度并发的 Java 应用程序,该应用程序使用光纤在内部将 http 请求发送到多个 HTTP 端点
  • 应用程序运行在 tomcat 之上(但是,纤程仅用于内部请求分派。tomcat servlet 请求仍然以标准阻塞方式处理)
  • 每个外部请求内部打开15-20个Fiber,每个Fiber构建一个HTTP请求并使用FiberHttpClient来调度它
  • 我正在使用 c44xlarge 服务器(16 核)来测试我的应用程序
  • 我连接的端点会抢占保持活动连接,这意味着如果我尝试通过重新使用套接字来维护,连接会在请求执行尝试期间关闭。因此,我禁用连接回收。
  • 根据上面的部分,这是我的光纤 http 客户端的调整(当然我使用的是单个实例):

    PoolingNHttpClientConnectionManager connectionManager = 
    new PoolingNHttpClientConnectionManager(
        new DefaultConnectingIOReactor(
            IOReactorConfig.
                custom().
                setIoThreadCount(16).
                setSoKeepAlive(false).
                setSoLinger(0).
                setSoReuseAddress(false).
                setSelectInterval(10).
                build()
                )
        );
    
    connectionManager.setDefaultMaxPerRoute(32768);
    connectionManager.setMaxTotal(131072);
    FiberHttpClientBuilder fiberClientBuilder = FiberHttpClientBuilder.
            create().
            setDefaultRequestConfig(
                    RequestConfig.
                    custom().
                    setSocketTimeout(1500).
                    setConnectTimeout(1000).
                    build()
            ).
           setConnectionReuseStrategy(NoConnectionReuseStrategy.INSTANCE).
           setConnectionManager(connectionManager).
           build();
    
  • 打开文件的 ulimit 设置得非常高(软值和硬值均为 131072)

  • Eden设置为18GB,总堆大小为24GB
  • 操作系统 Tcp 堆栈也经过了很好的调整:

内核.printk = 8 4 1 7 内核.printk_ratelimit_burst = 10 内核.printk_ratelimit = 5 net.ipv4.ip_local_port_range = 8192 65535 net.core.rmem_max = 16777216 网络核心.wmem_max = 16777216 net.core.rmem_default = 16777216 net.core.wmem_default = 16777216 net.core.optmem_max = 40960 net.ipv4.tcp_rmem = 4096 87380 16777216 net.ipv4.tcp_wmem = 4096 65536 16777216 net.core.netdev_max_backlog = 100000 net.ipv4.tcp_max_syn_backlog = 100000 net.ipv4.tcp_max_tw_buckets = 2000000 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_fin_timeout = 10 net.ipv4.tcp_slow_start_after_idle = 0 net.ipv4.tcp_sack = 0 net.ipv4.tcp_timestamps = 1

问题描述

  • 在中低负载下一切正常,连接被租用、关闭并且池得到补充
  • 超过某个并发点,IOReactor 线程(其中 16 个)似乎在死亡之前停止正常运行。
  • 我编写了一个小线程来获取池统计信息并每秒打印它们。在大约 25K 租用连接时,不再通过套接字连接发送实际数据,Pendingstat clibms 也导致 30K 待处理连接请求激增
  • 这种情况持续存在,基本上导致应用程序无用。在某些时候,I/O Reactor 线程会死掉,不确定何时,而且到目前为止我还无法捕获异常
  • lsof运行java进程,我可以看到它有数以万计的文件描述符,几乎所有它们都处于CLOSE_WAIT状态(这是有道理的,因为I/O反应器线程死亡/停止运行并且永远不会真正关闭它们
  • 在应用程序中断期间,服务器没有严重过载/CPU 压力

问题

  • 我猜我正在某个地方达到某种边界,尽管我对它可能存在的内容或位置相当无能为力。除以下情况外
  • 我是否有可能到达操作系统端口(毕竟所有应用请求都源自单个内部 IP)限制并创建一个错误,导致 IO Reactor 线程死亡(类似于打开文件限制错误)?

忘了回答这个问题,但我在发布问题后大约一周才知道发生了什么:

  1. 存在某种错误配置,导致 io-reactor 仅生成 2 个线程。

  2. 即使提供更多反应器线程后,问题仍然存在。事实证明,我们的传出请求大部分都是 SSL。 Apache SSL 连接处理将核心处理传播到 JVM 的 SSL 设施,这些设施的效率根本不足以每秒处理数千个 SSL 连接请求。更具体地说,SSLEngine 中的一些方法(如果我没记错的话)是同步的。在高负载下执行线程转储显示 IORecator 线程在尝试打开 SSL 连接时相互阻塞。

  3. 即使尝试以连接租约超时的形式创建压力释放阀也不起作用,因为创建的积压量太大,导致应用程序无用。

  4. 将 SSL 传出请求处理卸载到 nginx 的性能更差 - 因为远程端点会抢先终止请求,因此无法使用 SSL 客户端会话缓存(JVM 实现也是如此)。

最终在整个模块前面放置了一个信号量,在任何给定时刻将整个模块限制为 ~6000,这解决了问题。

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

高并发 Apache 异步 HTTP 客户端 IOReactor 问题 的相关文章

  • JPA 中的复合键

    我想创建一个具有自动生成的主键的实体 而且还有一个由其他两个字段组成的唯一复合键 我如何在 JPA 中执行此操作 我想这样做是因为主键应该用作另一个表中的外键 并且使其复合并不好 在下面的代码片段中 我需要命令和模型是唯一的 pk当然是主键
  • @RestController 没有 @ResponseBody 方法工作不正确

    我有以下控制器 RestController RequestMapping value base url public class MyController RequestMapping value child url method Req
  • 通过SOCKS代理连接Kafka

    我有一个在 AWS 上运行的 Kafka 集群 我想用标准连接到集群卡夫卡控制台消费者从我的应用程序服务器 应用程序服务器可以通过 SOCKS 代理访问互联网 无需身份验证 如何告诉 Kafka 客户端通过代理进行连接 我尝试了很多事情 包
  • 使用 Ant 将非代码资源添加到 jar 文件

    我正在将 java 应用程序打包成 jar 文件 我正在使用 ant 和 eclipse 我实际上需要在 jar 中直接在根文件夹下包含几个单独的非代码文件 xml 和 txt 文件 而不是与代码位于同一位置 我正在尝试使用includes
  • JVisualVM/JConsole 中的 System.gc() 与 GC 按钮

    我目前正在测试处理 XML 模式的概念验证原型 并围绕一个非常消耗内存的树自动机外部库 我已经获得了源代码 构建 我想绘制 真实峰值 堆 随着模式大小的增加 不同运行的内存消耗 使用的指标符合我的目的并且不会影响问题 或者至少是它的合理近似
  • Spring Data JPA 选择不同

    我有一个情况 我需要建立一个select distinct a address from Person a 其中地址是 Person 内的地址实体 类型的查询 我正在使用规范动态构建我的 where 子句并使用findAll Specifi
  • 使用 JUnit 时,有没有办法验证测试方法中是否调用了 try/catch 指令的 Catch 部分?

    例如 如果我想测试以下课程 public class SomeClass public void someMethod try Some code where comething could go wrong catch Exception
  • 需要使用 joda 进行灵活的日期时间转换

    我想使用 joda 解析电子邮件中的日期时间字符串 不幸的是我得到了各种不同的格式 例如 Wed 19 Jan 2011 12 52 31 0600 Wed 19 Jan 2011 10 15 34 0800 PST Wed 19 Jan
  • Jackson XML ArrayList 输出具有两个包装器元素

    我在 Jackson 生成的 XML 输出中得到了两个包装器元素 我只想拥有一个 我有一个 Java bean Entity Table name CITIES JacksonXmlRootElement localName City pu
  • 是否可以通过编程方式查找 logback 日志文件?

    自动附加日志文件以支持电子邮件会很有用 我可以以编程方式设置路径 如以编程方式设置 Logback Appender 路径 https stackoverflow com questions 3803184 setting logback
  • 如何检测 Java 字符串中的 unicode 字符?

    假设我有一个包含 的字符串 我如何找到所有这些 un icode 字符 我应该测试他们的代码吗 我该怎么做呢 例如 给定字符串 A X 我想将其转换为 AYXY 我想对其他 unicode 字符做同样的事情 并且我不想将它们存储在某种翻译映
  • 生成的序列以 1 开头,而不是注释中设置的 1000

    我想请求一些有关 Hibernate 创建的数据库序列的帮助 我有这个注释 下面的代码 在我的实体类中 以便为合作伙伴表提供单独的序列 我希望序列以 1000 开头 因为我在部署期间使用 import sql 将测试数据插入数据库 并且我希
  • 在另一个模块中使用自定义 gradle 插件模块

    我正在开发一个自定义插件 我希望能够在稍后阶段将其部署到存储库 因此我为其创建了一个独立的模块 在对其进行任何正式的 TDD 之前 我想手动进行某些探索性测试 因此 我创建了一个使用给定插件的演示模块 到目前为止 我发现执行此操作的唯一方法
  • 了解joda时间PeriodFormatter

    我以为我明白了 但显然我不明白 你能帮我通过这些单元测试吗 Test public void second assertEquals 00 00 01 OurDateTimeFormatter format 1000 Test public
  • 内部存储的安全性如何?

    我需要的 对于 Android 我需要永久保存数据 但也能够编辑 并且显然是读取 它 用户不应访问此数据 它可以包含诸如高分之类的内容 用户不得对其进行编辑 我的问题 我会 并且已经 使用过Internal Storage 但我不确定它实际
  • GWT 2.3 开发模式 - 托管模式 JSP 编译似乎不使用 java 1.5 兼容性

    无法编译 JSP 类 生成的 servlet 错误 DefaultMessage 上次更新 0 日期 中 0 时间 HH mm ss z 语法 错误 注释仅在源级别为 1 5 时可用 在尝试以开发模式在 Web 浏览器中打开我的 gwt 模
  • java库维护数据库结构

    我的应用程序一直在开发 所以偶尔 当版本升级时 需要创建 更改 删除一些表 修改一些数据等 通常需要执行一些sql代码 是否有一个 Java 库可用于使我的数据库结构保持最新 通过分析类似 db structure version 信息并执
  • 使用按钮作为列表的渲染器

    我想使用一个更复杂的渲染器 其中包含列表的多个组件 更准确地说 类似于this https stackoverflow com questions 10840498 java swing 1 6 textinput like firefox
  • 在浏览器刷新中刷新检票面板

    我正在开发一个付费角色系统 一旦用户刷新浏览器 我就需要刷新该页面中可用的统计信息 统计信息应该从数据库中获取并显示 但现在它不能正常工作 因为在页面刷新中 java代码不会被调用 而是使用以前的数据加载缓存的页面 我尝试添加以下代码来修复
  • 洪水填充优化:尝试使用队列

    我正在尝试创建一种填充方法 该方法采用用户指定的初始坐标 检查字符 然后根据需要更改它 这样做之后 它会检查相邻的方块并重复该过程 经过一番研究 我遇到了洪水填充算法并尝试了该算法 它可以工作 但无法满足我对 250 x 250 个字符的数

随机推荐