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

时间:2016-10-21 15:53:01

标签: java nio apache-httpclient-4.x quasar

申请说明:

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

问题描述

  • 在中低负荷下一切都很好,连接租赁,cloesd和池补充
  • 除了一些并发点之外,IOReactor线程(其中16个)在死亡之前似乎停止正常运行。
  • 我写了一个小线程来获取池统计信息并每秒打印一次。在大约25K的租用连接上,实际数据不再通过套接字连接发送,Pending stat clibms也是一个空中飙升的30K挂起连接请求
  • 这种情况持续存在,并且基本上使应用程序无用。在某些时候I / O Reactor线程死了,不知道什么时候到目前为止我还没有能够捕获异常
  • lsof在java进程中,我可以看到它有成千上万的文件描述符,几乎所有文件描述符都在CLOSE_WAIT中(这是有意义的,因为I / O反应器线程死/停止运行,从不实际上关闭它们
  • 在应用程序中断期间,服务器没有严重过载/ cpu压力

问题

  • 我猜我到达某个地方的某个边界,虽然我对它可能存在的地方或地点毫无头绪。除以下
  • 我是否有可能到达一个操作系统端口(所有应用程序请求都源自单个内部IP)限制并创建一个错误,将IO Reactor线程发送到die(类似于打开文件限制错误)?< / LI>

1 个答案:

答案 0 :(得分:0)

忘了回答这个问题,但在发布问题大约一个星期后我得到了什么:

  1. 有某种错过配置导致io-reactor只用2个线程产生。

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

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

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

  5. 在整个模块前面放置一个信号量,在任何给定时刻将整个事件限制在~6000左右,这就解决了这个问题。