java - 主机的动态地址移到另一个主机后,Java REST Client仍连接到旧主机

标签 java linux jersey

我正在开发具有以下特征的客户端和服务器系统:
客户端和服务器在Linux上运行。

有一对冗余服务器主机(用于硬件生存性)。
每个主机都有一个固定的IP地址。
还有一个浮动IP地址,一次绑定到一台主机。

在过渡期间,备用主机取消绑定浮动IP,然后新活动的主机将其绑定。
绑定是通过ifconfig up / down命令完成的。
配音-U用于使交换机知道与该地址关联的MAC已更改。

两台服务器主机都连接到同一台交换机。

在此系统中,不使用主机名(fqdn)。

服务器主机正在运行2个Java进程和一个Erlang进程。

客户端在erlang进程上打开了一个永久监听套接字,
并且还使用RMI接口和REST接口与Java进程进行通信。
Jersey v 1.17框架用于REST。
所有Java进程都在运行Java 1.6。

当客户端使用浮动地址连接到活动服务器时,原来一切正常。
在某一时刻,我强行切换了服务器。
前一个活动主机取消绑定该浮动地址,而前一个备用主机绑定到该地址。

有趣从这里开始。
根据客户端的运行位置,可以看到三种不同的行为。


A)客户端运行在与主机不同的网络上
(需要路由才能到达服务器)。
B)客户端正在原始备用主机上运行(请记住
两台服务器主机都位于同一网络/交换机上)。
C-客户端正在最初处于活动状态的主机上运行。


B和C是必需的用例。

案例A最容易解决。
一旦地址不受限制,我就会收到一个套接字异常,从连接到erlang进程,我可以做出反应。
只需稍等一会儿再重新绑定地址,然后重新连接即可解决此问题。
RMI界面相同,再次执行名称查找将返回指向新的活动主机的存根
(存根指向固定地址-我可以接受)
然后,当我发出REST请求时,它们也将路由到新的活动主机。

情况B提供了稍有不同的错误,并且心跳在检测到更改中起作用,
但是解决方法是一样的。

违反我的理解的案例是案例C。
在这种情况下,与erlang进程的连接已从本地主机正确转移到新的活动主机。
还可以从新主机完成RMI名称查找。

但是,REST调用从本地主机(以前的活动主机)而不是新的活动主机返回数据。
这种情况会持续几分钟(通常是3-4分钟),然后情况会自行修复!
我非常努力地尝试找出这一点,进行测试,进行网络挖掘,到目前为止还算不上运气。

我认为这种行为可能来自3个地方:


操作系统(即内核tcp / ip处理)
Java VM或其标准库
泽西岛图书馆


我提出了许多假设:


Linux内核IP堆栈正在缓存连接,但没有
意识到地址已经移动,直到缓存超时。一世
尝试了各种命令来冲洗它;不行事实
与erlang的连接以及名称查找有效,这使我倾向于
相信内核不会在这里造成问题。
我认为可能有一些特权的Java东西
发生在Java或VM的胆量中。名称的事实
查找有效,也可能使它无效。
我的最后一个假设是Jersey正在进行某种缓存。
Web正在谈论Http连接池。其实那个
可能是Java完成的HttpUrlConnection池。我尝试过了
了解有关此内容的更多信息,但仅找到模糊的参考。但是大多数
我得到的信息表明应该打开套接字,
对于每个REST请求都已关闭。


到现在为止,我已经绝望了,准备考虑任何假设
;-)

任何人都可以阐明一种机制,该机制将继续将数据包路由到错误的主机
地址移到主机之后。

对于3到4分钟后似乎消失的事实,我们能说些什么。
这可能是哪种类型的缓存。

谢谢大家的提示。

最佳答案

我设法找到更多时间来研究这个问题。

我对某些人对HttpUrlConnection实例正在使用的套接字池的引用进行了更多搜索。

我发现的有趣链接之一是:

http://docs.oracle.com/javase/6/docs/technotes/guides/net/http-keepalive.html

它确认默认情况下为HTTP连接保留了一个套接字池。
基础套接字保持活动状态。

这意味着,当您对每个请求使用新的HttpUrlConnection实例时,
您可能会重用已经打开的套接字。
这使客户端更有效,尤其是在使用HTTPS的情况下。

在我的情况下(用例C),Linux提供了一个套接字,该套接字在两个本地进程之间保持功能,
即使用于打开它的原始IP地址消失了。

这意味着先前从池中打开的套接字现在指向错误的主机。
(即,本地主机不再与该地址相关联,而是新主机)。

这就解释了我所看到的行为(最终!)。

修复很容易。

Oracle的文章指出,您可以设置系统属性http.keepAlive
为false(默认为true)。

System.setProperty("http.keepAlive", "false");


这使系统为每个HTTP请求打开一个新的套接字。
您失去了效率,但得到了一个可以幸免于地址迁移的客户。
就我而言,这很关键(但可能不是常见情况)。

本文还提到了您可以用来控制池的其他一些操作(例如,其大小)。

一种替代方法是在地址切换上发送HTTP HEAD,并将元数据连接设置为关闭。
正确执行此操作比较棘手,但大多数时候都可以保留客户的效率。

如果有人有其他见解,请随时提供。

关于java - 主机的动态地址移到另一个主机后,Java REST Client仍连接到旧主机,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22597621/

相关文章:

java - 无效的 Xsl 格式(或)文件名

java - 如何搜索存储在 hashmap 中的匹配对?

java - 将 Jersey 客户端添加到我的 Android 应用程序。发生非法参数异常?

java - 如何在 Java Jersey 中获取 HTML 选中的复选框

java - Java 的桌面库能否在新的浏览器选项卡或窗口中启动 URL?

使用 Kryo 序列化对象时出现 java.lang.StackOverflowError

linux - 在 'grep' 中 - 为什么它会这样?

linux - 如何在 Linux 中监视完整的目录树的更改?

python - 您如何使用 Python 与自定义 DBUS 对象对话?

java - 为什么 Jersey 不尊重动态绑定(bind)过滤器中的优先级?