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

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

现在的问题是,当haproxy关闭连接以实现重新平衡时,它会通过发送tcp-RST来实现。 当netty使用的sun.nio.ch-class看到这个时,它会抛出一个IOException:“Connection 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个exception。 此外,它有效地进行了网络负载平衡,因为一些传入连接非常长,并且具有非常高的吞吐量:使用pretend-keepalive,它们永远不会被haproxy重新平衡到另一台服务器。

选项http-keep-alive

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

选项http-server-close

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

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

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

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

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

我们有一个系统,其中连接始终保持活动状态(在HTTP连接级别保持活动始终是正确的。即,一旦建立连接,我们就会从HTTP连接池重用此连接以用于后续调用而不是创建新连接。)

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

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

  2. 当没有设置HAProxy的选项prefer-last-server时。
    根据文件:

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

由于未设置此选项,因此每次从池中重新使用连接时,HAProxy都会使用RST Signal关闭此连接,并为不同的服务器创建一个新连接( 因为我们的Load Balancer设置为循环 )。 这搞乱了,整个连接池无法使用。

所以配置工作正常:

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

有了这些配置,我们就可以

  • 消除连接重置
  • 获取连接池工作
  • 确保负载均衡在服务器之间均匀发生,无论它们何时启动。

希望这可以帮助!!

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)的初始抛出真的是不可避免的,除非你自己重新实现它们。

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

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

如果它能找到这个记录,我会添加链接…

试试吧

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

不确定redispatch,但http-tunnel修复了我们的问题。