应用说明:
- 我正在使用由 Comsat 的 Quasar FiberHttpClient(版本 0.7.0)包装的 Apache HTTP 异步客户端(版本 4.1.1)来运行和执行一个高度并发的 Java 应用程序,该应用程序使用光纤在内部将 http 请求发送到多个 HTTP 端点
- 应用程序在 tomcat 之上运行(但是,fiber 仅用于内部请求调度。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();
打开文件的 ulimits 设置为超高(软和硬值均为 131072)
- Eden 设置为 18GB,总堆大小为 24GB
- OS Tcp 堆栈也经过了很好的调整:
kernel.printk = 8 4 1 7 kernel.printk_ratelimit_burst = 10 kernel.printk_ratelimit = 5 net.ipv4.ip_local_port_range = 8192 65535 net.core.rmem_max = 16777216 net.core.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 的租用连接处,不再通过套接字连接发送实际数据,该
Pending
统计数据也向猛增的 30K 未决连接请求发送 - 这种情况持续存在并且基本上使应用程序无用。在某些时候,I/O Reactor 线程会死掉,不确定何时,到目前为止我还无法捕捉到异常
lsof
在调用 java 进程时,我可以看到它有数以万计的文件描述符,几乎所有文件描述符都在 CLOSE_WAIT 中(这是有道理的,因为 I/O 反应器线程死亡/停止运行并且永远不会真正关闭它们- 在应用程序中断期间,服务器没有严重过载/cpu 压力过大
问题
- 我猜我正在某个地方到达某种边界,尽管我对它可能驻留的内容或位置一无所知。下列情况除外
- 我是否有可能到达操作系统端口(毕竟所有应用请求都源自单个内部 IP)限制并创建一个错误,导致 IO Reactor 线程死亡(类似于打开文件限制错误)?