Java 如何确保RMI操作的原子性?

Java 如何确保RMI操作的原子性?,java,rmi,Java,Rmi,我想使用RMI更新文件。如何确保操作状态 客户端连接到服务器并调用方法。紧接着,客户端和服务器之间的连接断开,客户端得到一个RemoteException,但被调用的方法继续工作,并更改一个文件,然后返回并得到异常(假设在将响应写入套接字时会实现连接丢失)。这在实践中相当复杂。您需要实现一个两阶段提交协议,在该协议中,客户机可以获得服务器可以保证提交的指示符,然后对提交进行排序 有一个现成的协议用于管理此过程,它由支持。基本上,您可以为您的文件构建一个 这不是一项微不足道的任务,但如果您不介意在

我想使用RMI更新文件。如何确保操作状态


客户端连接到服务器并调用方法。紧接着,客户端和服务器之间的连接断开,客户端得到一个
RemoteException
,但被调用的方法继续工作,并更改一个文件,然后返回并得到异常(假设在将响应写入套接字时会实现连接丢失)。

这在实践中相当复杂。您需要实现一个两阶段提交协议,在该协议中,客户机可以获得服务器可以保证提交的指示符,然后对提交进行排序

有一个现成的协议用于管理此过程,它由支持。基本上,您可以为您的文件构建一个

这不是一项微不足道的任务,但如果您不介意在基础架构中包含应用程序服务器或事务管理器,那么这并不是不可能的。这种方法的优点是,您可以连接到成熟的、经过调试的事务管理基础架构中

有两个备选方案是:

  • 实现您自己的两阶段提交协议,或者

  • 构造文件更新代码,使其是幂等的(多次调用以进行相同的更新,只需进行该更新)。在这种情况下,您可以重新尝试更新,直到您的客户端从服务器获得成功指示。

    请注意,您还必须制定一个协议,用于锁定和/或管理同一记录上的冲突更新,除非您能保证客户端的顺序写入。根据应用程序的不同,这可能是一个乐观并发协议,但在对其进行操作时,仍然必须锁定正在处理的范围,以使写入成为原子


    • 您正在处理分布式处理。这意味着您不能保证特定消息(方法调用等)将到达另一端;连接总是可以任意断开。因此,您需要一种不依赖任何特定消息到达的缓解策略

      其中一种策略是使用可靠的消息传递系统,该系统基本上将数据库放置在消息队列中,然后可靠地按顺序传递消息(通过重复发送消息(用序列标识符标记),直到收到确认),但这是一个很大的开销,并且可能应保存用于关键事项(例如,金融交易)

      另一种策略是使用分布式事务管理器,但这些事务管理器存在相当大的问题(除非您实现分布式一致性系统,这很复杂,并且仍然存在潜在的故障模式)


      我认为最简单的方法是重新组织那些被认为是确定的东西。让客户机与服务器通信,以汇编要在服务器上提交的操作的临时描述(包括唯一id,例如UUID),然后客户机可以发送启动提交的短消息;此时,服务器需要记录它已开始处理该UUID。如果无法发送响应,则仍在提交之后。如果有任何消息丢失,那也没什么大不了的:要么是在提交开始之前(在这种情况下,它没有发生,可以重试),要么是在提交之后,会有一个永久记录,表明它被尝试过,并且很容易报告发生了什么(“我仍在努力”、“我成功了”、“我失败了”)。客户机需要记住的唯一状态是UUID,它可以在事务外部分配(UUID的一个极好的属性,虽然在概率上是正确的,但出现问题的可能性确实可以忽略不计)。

      您可以在每次操作中从客户机向服务器传递一个顺序计数器参数。根据您的使用场景,服务器需要记住最近发送的一个或多个计数器。如果看到一个已经发生的值,您可以返回success(或其他)。如果这些值需要在多个客户端之间是唯一的,那么可以使用UUID之类的东西(另一个答案提到)。这在很大程度上取决于返回值和调用序列的外观


      作为一个例子,我在项目中实现了这个想法来处理这个问题。在RMIO库中,您只需处理“当前”操作被重复的可能性,因此您只需跟踪最后一个计数器值。

      如果您选择滚动您自己的伪事务系统,以下是基本操作:

      在服务器端:

      • 执行从服务器到客户端的回调,以宣布您已准备好写入文件。如果出现错误,请中止该过程
      • 只有在回调成功时,才实际写入文件
      在客户机上:

      • 当您收到RemoteException时,请检查是否执行了回调
      • 如果执行了回调,您可以“非常确定”过程的其余部分将被执行,并且RemoteException是由于其他原因发生的

      在这种情况下,客户端不会出现异常,除非您配置了响应超时:可以这样做,但没有记录。默认情况下,客户端将永远等待。@EJB我认为客户端不会永远等待。除非设置响应超时,否则它可能会永远阻止等待响应到达。