跨网络在java实例之间同步变量

时间:2017-05-15 21:44:10

标签: java sockets

我在大学里有这个任务,他们要求我们将Java应用程序作为具有多个客户端的套接字服务器运行。客户端发送一个字符串,服务器以大写形式返回带有请求计数器的字符串。非常简单。

任何给定客户端发出的每个请求都在服务器端计算,并存储在每个客户端连接线程的静态变量中。这样每个客户端请求都会在服务器上全局递增计数器。这很好用。

现在,他们要求我们运行" backup"网络上不同计算机上的该服务器的实例,以便在主服务器停止响应时,客户端连接到其中一个备份。那,我开始工作了。但计数器显然是重置的,因为它是一个不同的服务器。

挑战在于主要和次要请求计数器是相同的,因此如果主要响应10个请求,关闭,客户端切换到备份并发出请求,则备份服务器响应11。

以下是我的想法:

  1. 如果在同一台PC上,我使用线程,但我们通过网络,所以我 相信这不会奏效。
  2. 服务器将该计数器发送给客户端 响应,然后将其返回到服务器 下一个请求等等。不是"清洁" imo但可以工作。
  3. 每个服务器相互通信以同步此计数器。但是,套接字 如果可能的话,似乎不是很有效率。 RMI 这似乎是一个替代方案,但我想在我之前做出确认 开始学习它。
  4. 这里有任何线索或建议吗?我没有发布代码,因为我不需要这里的解决方案,但如果有必要,我可以邀请到gihub回购。

    编辑:此项目没有延迟,可靠性或类似限制。有X个客户端和Y个服务器(单个主服务器,多个故障转移)。像DB这样的其他第三方基础设施实际上并不是一个选择,但欢迎使用第三方Java库。基本上我只是在多台PC上运行Eclipse。这是分布式系统的介绍任务,预计在2周内完成,所以我相信"保持简单"是关键在这里!

    编辑2:备份服务器的数量和地址将作为参数传递给应用程序,因此不需要广播/发现。在本学期的后期实验作业中,我们可能会涵盖所有这些要点:)

    编辑3 :根据您的所有好建议,我将尝试实施#3的某些变体,并告诉您它是如何工作的。我认为我在这里遇到的问题是确保所有服务器都知道其他服务器。但就像我提到的那样,他们不需要发现彼此,所以我现在要对它进行硬编码,并在下一个任务中重新考虑!可能选择一些当选的主人......:)

5 个答案:

答案 0 :(得分:2)

如果允许选项#2,那么它是最简单的,但是我不确定它如何在多个客户端面前工作(所以它取决于这里的要求)。

是否可以通过在另一台计算机上运行的共享数据库来支持服务器?理想情况下,也许一个群集在多台机器上?或者您可以使用事件总线或第三方库/代码(共享缓存,JMS甚至EJB)吗?

如果没有,那么让服务器互相交谈是最好的选择。套接字可以工作,UDP多播也可以工作(但要小心,无法知道消息是否丢失,这就是TCP /套接字更安全的原因)。如果节点要相互通信,通常有一些可接受的方法来处理设置:

  • 主/从:当前节点是主节点,所有写入都是它。从站连接到主站并接收更新。当主人失败时,需要选出新的主人(见领导人选举)。 MongoDB就是这样的。
  • 每个人都是:每个节点都连接到其他所有已知节点。可能会变得复杂,并且可能无法很好地扩展到许多节点。
  • 菊花链:一个节点连接到下一个节点,该节点连接到下一个节点,依此类推。我不相信这被广泛使用。
  • 环形网络:每个节点连接到另外两个节点以形成环。这通常优于菊花链,但实现起来要复杂一些。

有关更多示例,请参阅此处:https://en.wikipedia.org/wiki/Network_topology

如果这是在现实世界中(即不是学校),您将使用共享缓存(例如ehcache),由事件总线支持的本地缓存(某种JMS)或共享集群数据库。

修改

重新阅读您的问题后,您似乎只需要担心一台备份服务器,我对课程要求的猜测是他们只是希望您的备份服务器连接到您的主服务器并且还接收变量计数更新。使用套接字实现这一点非常好(单个备份服务器效率不高),也许是他们期望您使用的解决方案。

E.g。备份服务器连接到主服务器,并在所保持的连接中轮询更新,或者只是侦听从主服务器发出的更新。

主要提示:   - 您可能需要保持活力以确保连接不会被杀死。   - 如果从备份到主服务器的连接死亡,请确保实现重新连接逻辑。

如果这是针对网络课程,他们可能期望UDP多播,但这可能取决于服务器/网络环境。

答案 1 :(得分:1)

这是一个典型的分布式系统问题。正确的解决方案是您的选项#3的一些变体,其中不同的服务器相互通信。

当您开始在各种服务器之间引入延迟,停机时间和/或网络分区时,它变得复杂。最终你需要到达某种consensus algorithmPaxos是解决这个问题的一个众所周知的方法,但还有其他方法; Raft这些天也很受欢迎。

答案 2 :(得分:1)

在我看来,最好的解决方案是拥有计数器的向量。每台服务器一个计数器。每个服务器将其自己的计数器和广播向量值递增到所有其他服务器。此数据结构为conflict-free replicated data type

请求数量计算为向量的所有元素的总和。

关于一致性。如果您需要在所有服务器上严格增加数量,则需要在回答客户端之前同步复制新值。 这里的惩罚是性能和可用性。

关于广播。您可以选择所需的任何广播算法。如果服务器数量不是太大,您可以使用全网状拓扑。如果服务器数量变大,您可以使用环形或星形拓扑来复制数据。

答案 3 :(得分:0)

最真实的生活将是选项3.它一直在发生。节点在另一个端口上相互通信。所以他们通过广播(UDP)自我发现。因此,每个服务器广泛在UDP端口上投射其最大值。如果其当前值小于该值,则其他节点监听并将其值+ 1,否则忽略它,而是广播其更大的值。

当客户端呼叫之间存在2-300的差距时,这将最有效。这也假定任何服务器都可以是主服务器(由负载均衡器决定)。

UDP在局域网内稳定。广泛使用。

答案 4 :(得分:0)

此问题的解决方案可以加快速度与一致性。

如果您重视速度的一致性,可以尝试同步方法(假设服务器A,B和C):

  1. A收到初始请求
  2. A打开与B和C的连接,以请求每个
  3. 的当前计数
  4. A计算最大计数(基于其自身的值和B和C的值),加1并将新计数发送到B和C
  5. A关闭与B和C的连接
  6. 对原始请求的回复,包括新的最大数量
  7. 此时,所有服务器都与新的最大计数同步,准备好向任何服务器发出新请求。

    编辑:当然,如果您能够使用共享数据库,问题会变得更加简单。