Haproxy + netty:防止连接重置异常的方法?

时间:2014-02-04 10:49:36

标签: java tcp netty nio haproxy

我们在netty-3.6运行的后端使用haproxy。我们正在处理大量的连接,其中一些可能是长期存在的。

现在的问题是,当haproxy关闭连接以实现重新平衡时,它会通过发送tcp-RST来实现。当netty使用的sun.nio.ch-class看到这个时,会抛出一个IOException:“peer by peer”。

跟踪:

sun.nio.ch.FileDispatcherImpl.read0(Native Method):1 in ""
sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:39):1 in ""
sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:225):1 in ""
sun.nio.ch.IOUtil.read(IOUtil.java:193):1 in ""
sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:375):1 in ""
org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:64):1 in ""
org.jboss.netty.channel.socket.nio.AbstractNioWorker.process(AbstractNioWorker.java:109):1 in ""
org.jboss.netty.channel.socket.nio.AbstractNioSelector.run(AbstractNioSelector.java:312):1 in ""
org.jboss.netty.channel.socket.nio.AbstractNioWorker.run(AbstractNioWorker.java:90):1 in ""
org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:178):1 in ""
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145):1 in ""
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615):1 in ""
java.lang.Thread.run(Thread.java:724):1 in ""

这会导致每个配置出现以下问题:

  

选项http-pretend-keepalive

这是最好的(因为haproxy似乎关闭了大多数与FIN而不是RST的连接),但仍然每个服务器每秒产生大约3个异常。此外,它实际上是neuters负载平衡,因为一些传入连接非常长,并且具有非常高的吞吐量:使用pretend-keepalive,它们永远不会被haproxy重新平衡到另一台服务器。

  

选项http-keep-alive

由于我们的后端期望保持活动连接真正保持活动(因此不会自行关闭它们),因此此设置相当于每个连接最终会导致一个异常,从而导致我们的服务器崩溃。我们尝试添加prefer-last-server,但它没有多大帮助。

  

选项http-server-close

理论上,这应该适用于正确的负载均衡和无异常。但是,似乎在我们的后端服务器响应之后,有一个竞争对手首先发送其RST:haproxy或我们注册的ChannelFutureListener.CLOSE。在实践中,我们仍然会遇到太多异常而且我们的服务器崩溃了。

有趣的是,例外情况通常会越多,我们为渠道提供的工作人员就越多。我想它的阅读速度超过了写作速度。

无论如何,我已经阅读了netty中的不同频道和套接字以及haproxy一段时间了,并没有找到任何听起来像解决方案的东西(或者在我尝试时工作)。

5 个答案:

答案 0 :(得分:6)

注意:根据我的理解,您不必担心连接重置异常,除非您最后使用保持连接连接建立连接池。

在使用HAProxy进行服务时,我遇到了类似的问题大量连接重置(RST)(在10秒的窗口中,基于负载,它是5-20倍)。
这就是我修复它的方法。

我们有一个系统,其中连接始终保持活动状态(在HTTP连接级别保持活动始终为真。即,一旦建立连接,我们就会重新使用HTTP Connection pool中的此连接进行后续调用,而不是创建新的。)

现在,根据我的调试代码 TCP Dump ,我发现RST是在下面的场景中从HAProxy抛出的

  1. 在空闲连接时,当HAProxy的timeout clienttimeout server到达时。
    我们将此配置设置为60秒。由于我们有一个连接池,当服务器上的负载减少时,会导致其中一些连接在一分钟内没有被使用。
    因此,这些连接由HAProxy使用RST信号关闭。

  2. 未设置HAProxy的option prefer-last-server时 根据文件:

  3.   

    真正的用途是发送到服务器的保持活动连接。当这个   使用选项,haproxy将尝试重用相同的连接   连接到服务器而不是重新平衡到另一台服务器,   导致关闭。

    由于未设置此项,因此每次从池中重新使用连接时,HAProxy都会使用RST Signal关闭此连接并为另一台服务器创建一个新连接(因为我们的Load Balancer已设置为圆形-Robin )。 这使得整个Connection Pooling变得混乱并无法使用。

    所以配置工作正常:

    1. option prefer-last-server:因此将重新使用与服务器的现有连接。
      注意:这不会导致负载均衡器在新服务器上使用以前的服务器进行新连接。新连接的决策始终基于负载平衡算法。此选项仅适用于客户端和服务器之间已存在的现有连接。
      当我使用此选项进行测试时,即使之前的连接已发送到server1,新连接仍将转到server2。
    2. balance leastconn:使用循环保持活跃,可能会出现与单个服务器的连接偏差。 (假设只有2台服务器,当一台服务器因部署而关闭时,所有新连接都将开始转移到另一台服务器。所以即使server2启动,循环仍然会向server1分配一个新请求,一分配给或者尽管server1在其末端有很多连接。所以服务器的负载永远不会完全平衡。)。
    3. 将HAProxy的timeout clienttimeout server设置为10分钟。这增加了我们的连接闲置的时间。
    4. 实现IdleConnectionMonitor:当超时设置为10m时,HAProxy的RST可能会减少但不会被消除。
      为了完全删除它,我们添加了一个负责的IdleConnectionMonitor关闭闲置超过9分钟的连接

    5. 通过这些配置,我们可以

      • 消除连接重置
      • 获取连接池工作
      • 确保负载均衡在服务器上均匀发生,无论他们何时开始。

      希望这会有所帮助!!

答案 1 :(得分:2)

Tomcat Nio-handler正好:

} catch (java.net.SocketException e) {
    // SocketExceptions are normal
    Http11NioProtocol.log.debug
        (sm.getString
         ("http11protocol.proto.socketexception.debug"), e);

} catch (java.io.IOException e) {
    // IOExceptions are normal
    Http11NioProtocol.log.debug

        (sm.getString
         ("http11protocol.proto.ioexception.debug"), e);

}

所以看起来内部sun-class(s​​un.nio.ch.FileDispatcherImpl)的初始抛出真的是不可避免的,除非你自己重新实现它们。

答案 2 :(得分:0)

'通过对等方重置Connecton'通常是由写入已被另一端关闭的连接引起的。这会导致对等体发送RST。但几乎可以肯定已经发送了一个FIN。我会在这里重新审视你的假设。很少有应用程序故意发送RST。您最可能遇到的是应用程序协议错误。如果这是不可避免的,那么ECONNRESET也是如此。

答案 3 :(得分:0)

尝试

  • 选项http-tunnel
  • 没有选项redispatch

不确定重新发送,但 http-tunnel 修复了我们的问题。

答案 4 :(得分:0)

从haproxy 1.5开始,它现在将FINFIN,ACK)发送到后端服务器,而harpoxy 1.4用于发送RST。这可能对这种情况有所帮助。

如果可以找到此文件,我将添加链接...