如何避免 NoRouteToHostException?

Posted

技术标签:

【中文标题】如何避免 NoRouteToHostException?【英文标题】:How to avoid a NoRouteToHostException? 【发布时间】:2010-12-07 00:45:04 【问题描述】:

披露:我正在编写的代码用于大学课程。

背景:我要完成的任务是报告不同线程技术的效果。为此,我编写了几个类,它们使用 Java 套接字响应来自客户端的请求。这个想法是用请求淹没服务器并报告不同的线程策略如何处理这个问题。每个客户端将发出 100 个请求,在每次迭代中,我们将客户端数量增加 50 个,直到出现问题。

问题:可重复且始终如一地发生异常:

引起:java.net.NoRouteToHostException:无法分配请求的地址 在 java.net.PlainSocketImpl.socketConnect(本机方法) 在 java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)

这发生在几种情况下,包括客户端和服务器都在本地主机上运行时。连接一段时间可以成功,尝试连接150个客户端后很快就会抛出异常。

我的第一个想法是这可能是 Linux 对打开文件描述符 (1024) 的限制,但我不这么认为。我还检查了套接字之间的所有连接是否正确关闭(即在正确的finally 块内)。

我对发布代码犹豫不决,因为我不确定哪些部分最相关,并且不想在问题中列出大量代码。

以前有人遇到过这种情况吗?


编辑(其他问题用斜体表示)

到目前为止,一些好的答案指向临时端口范围或 RFC 2780。这两者都表明我打开了太多连接。对于这两种情况,似乎需要建立的连接数才能达到此限制表明在某些时候我不会关闭连接。

在调试了客户端和服务器后,观察到两者都命中了方法调用myJava-Net-SocketInstance.close()。这表明连接正在关闭(至少在非异常情况下)。 这是一个正确的建议吗?

另外,是否需要等待操作系统级别的端口才能再次可用? 如果只需要很短的时间,则可以为每 50 多个客户端单独运行该程序在运行下一次尝试之前的一段时间(或者乐观地说,运行命令)。


编辑 v2.0

在接受了所提供的良好答案后,我修改了我的代码以使用 setReuseAddress(true) 方法与客户端上建立的每个 Socket 连接。这并没有达到预期的效果,我仍然限于 250-300 个客户。程序终止后,运行netstat -a命令显示有很多处于TIME_WAIT状态的socket连接。

我的假设是,如果套接字处于 TIME-WAIT 状态,并且已使用 SO-REUSEADDR 选项进行设置,则任何尝试使用该端口的新套接字都能够 - 但是,我仍然收到 NoRouteToHostException .

这是正确的吗? 还有什么办法可以解决这个问题吗?

【问题讨论】:

@Grundlefleck 如果您还没有这样做,请尝试调用默认的 Socket() ctor,以便返回未连接的套接字。然后设置重用地址(真)。然后连接()。您想告诉堆栈在尝试绑定之前重用该地址。 我之前调用了连接 (Socket(host,port)) 的构造函数,当我看到你的评论时,一个灯泡亮了,我为自己是个白痴打了自己一巴掌……但它没有不行,问题依然存在:-( 【参考方案1】:

您是否尝试过设置:

echo "1" >/proc/sys/net/ipv4/tcp_tw_reuse

和/或

echo "1" >/proc/sys/net/ipv4/tcp_tw_recycle

这些设置可能会使 Linux 重新使用 TIME_WAIT 套接字。不幸的是,我找不到任何明确的文档。

【讨论】:

对不起,我不得不说:我爱你! @atomice 老兄,非常感谢。一个星期以来我一直面临这个问题。你终于救了我。 这解决了困扰我们数月的令人讨厌的问题。 强制警告,如果某些连接超出负载平衡器,启用这些将导致问题。请参阅vincent.bernat.im/en/blog/2014-tcp-time-wait-state-linux.html 特别是,当远程主机实际上是 NAT 设备时,时间戳条件将禁止除 NAT 设备后面的主机之外的所有主机在一分钟内连接,因为它们不共享相同的时间戳时钟.毫无疑问,禁用此选项要好得多,因为它会导致难以检测和诊断问题。 此更改是否需要重新启动?【参考方案2】:

这可能会有所帮助:

The Ephemeral Port Range

另一个重要的分支 临时端口范围是它限制 最大连接数 一台机器上的特定服务 远程机器! TCP/IP 协议 使用连接的 4 元组 区分连​​接,所以如果 临时端口范围只有 4000 端口宽,这意味着可以 只有 4000 个唯一连接来自 客户端机器到远程服务 一次。

所以也许你用完了可用的端口。要获取可用端口的数量,请参阅

$ cat /proc/sys/net/ipv4/ip_local_port_range 
32768   61000

输出来自我的 Ubuntu 系统,我有 28,232 个端口用于客户端连接。因此,一旦您有 280 多个客户,您的测试就会失败。

【讨论】:

或者您可能已达到文件描述符的最大限制。请参阅 Linux 上的 ulimit 或 MacOS 上的 launchtl (docs.basho.com/riak/latest/ops/tuning/open-files-limit/…) 抱歉,这里的问题可能很愚蠢,但我们是如何获得 280 多个客户的? @ShaikZakirHussain 问题指出“每个客户端将发出 100 个请求”,因此 28,232 / 100 约为 280【参考方案3】:

Cannot assign requested address 是 EADDRNOTAVAIL 错误的错误字符串。

我怀疑您的源端口用完了。动态范围内有 16,383 个套接字可用作源端口(请参阅RFC 2780)。 150 个客户端 * 100 个连接 = 15,000 个端口 - 所以你可能已经达到了这个限制。

【讨论】:

【参考方案4】:

如果您的源端口用完了,但实际上并没有维护那么多打开的连接,请设置SO_REUSEADDR 套接字选项。这将使您能够重用仍处于TIME_WAIT 状态的本地端口。

【讨论】:

抱歉将其保持在 Java 级别。当我实例化套接字时,我还调用了 mySocket.setReuseAddress(true);这并不能解决问题。是否必须绑定端口才能使用它? 没有。如果您没有明确地 bind(),堆栈将在 connect() 期间为您执行此操作。【参考方案5】:

如果您每秒关闭 500 个连接,您将用完套接字。如果您连接到使用 keepalive 的相同位置(Web 服务器),您可以实现连接池,因此您无需关闭和重新打开套接字。

这也将节省 cpu。

使用 tcp_tw_recycle 和 tcp_tw_reuse 会导致数据包从之前的连接进来,这就是为什么要等待 1 分钟才能清除数据包。

【讨论】:

【参考方案6】:

对于遇到此问题的任何其他 Java 用户,我建议使用连接池,以便正确重用连接。

【讨论】:

c3p0 连接池是用于 JDBC 连接池的,不是问题所涉及的连接类型。

以上是关于如何避免 NoRouteToHostException?的主要内容,如果未能解决你的问题,请参考以下文章

如何避免重复的工作?

DDoS攻击是啥?应该如何避免?

Mongodb 连接失败,怎么避免抛错影响正常流程

Java中,当的数据过大时,如何避免值的改变?

如何避免嵌套订阅超过两个级别

如何触发(不避免!) HttpClient 死锁