Java 如何确保RMI操作的原子性?
我想使用RMI更新文件。如何确保操作状态Java 如何确保RMI操作的原子性?,java,rmi,Java,Rmi,我想使用RMI更新文件。如何确保操作状态 客户端连接到服务器并调用方法。紧接着,客户端和服务器之间的连接断开,客户端得到一个RemoteException,但被调用的方法继续工作,并更改一个文件,然后返回并得到异常(假设在将响应写入套接字时会实现连接丢失)。这在实践中相当复杂。您需要实现一个两阶段提交协议,在该协议中,客户机可以获得服务器可以保证提交的指示符,然后对提交进行排序 有一个现成的协议用于管理此过程,它由支持。基本上,您可以为您的文件构建一个 这不是一项微不足道的任务,但如果您不介意在
客户端连接到服务器并调用方法。紧接着,客户端和服务器之间的连接断开,客户端得到一个
RemoteException
,但被调用的方法继续工作,并更改一个文件,然后返回并得到异常(假设在将响应写入套接字时会实现连接丢失)。这在实践中相当复杂。您需要实现一个两阶段提交协议,在该协议中,客户机可以获得服务器可以保证提交的指示符,然后对提交进行排序
有一个现成的协议用于管理此过程,它由支持。基本上,您可以为您的文件构建一个
这不是一项微不足道的任务,但如果您不介意在基础架构中包含应用程序服务器或事务管理器,那么这并不是不可能的。这种方法的优点是,您可以连接到成熟的、经过调试的事务管理基础架构中
有两个备选方案是:
- 实现您自己的两阶段提交协议,或者
- 构造文件更新代码,使其是幂等的(多次调用以进行相同的更新,只需进行该更新)。在这种情况下,您可以重新尝试更新,直到您的客户端从服务器获得成功指示。
请注意,您还必须制定一个协议,用于锁定和/或管理同一记录上的冲突更新,除非您能保证客户端的顺序写入。根据应用程序的不同,这可能是一个乐观并发协议,但在对其进行操作时,仍然必须锁定正在处理的范围,以使写入成为原子 - 执行从服务器到客户端的回调,以宣布您已准备好写入文件。如果出现错误,请中止该过程
- 只有在回调成功时,才实际写入文件
- 当您收到RemoteException时,请检查是否执行了回调李>
- 如果执行了回调,您可以“非常确定”过程的其余部分将被执行,并且RemoteException是由于其他原因发生的
- 您正在处理分布式处理。这意味着您不能保证特定消息(方法调用等)将到达另一端;连接总是可以任意断开。因此,您需要一种不依赖任何特定消息到达的缓解策略
其中一种策略是使用可靠的消息传递系统,该系统基本上将数据库放置在消息队列中,然后可靠地按顺序传递消息(通过重复发送消息(用序列标识符标记),直到收到确认),但这是一个很大的开销,并且可能应保存用于关键事项(例如,金融交易)
另一种策略是使用分布式事务管理器,但这些事务管理器存在相当大的问题(除非您实现分布式一致性系统,这很复杂,并且仍然存在潜在的故障模式)
我认为最简单的方法是重新组织那些被认为是确定的东西。让客户机与服务器通信,以汇编要在服务器上提交的操作的临时描述(包括唯一id,例如UUID),然后客户机可以发送启动提交的短消息;此时,服务器需要记录它已开始处理该UUID。如果无法发送响应,则仍在提交之后。如果有任何消息丢失,那也没什么大不了的:要么是在提交开始之前(在这种情况下,它没有发生,可以重试),要么是在提交之后,会有一个永久记录,表明它被尝试过,并且很容易报告发生了什么(“我仍在努力”、“我成功了”、“我失败了”)。客户机需要记住的唯一状态是UUID,它可以在事务外部分配(UUID的一个极好的属性,虽然在概率上是正确的,但出现问题的可能性确实可以忽略不计)。您可以在每次操作中从客户机向服务器传递一个顺序计数器参数。根据您的使用场景,服务器需要记住最近发送的一个或多个计数器。如果看到一个已经发生的值,您可以返回success(或其他)。如果这些值需要在多个客户端之间是唯一的,那么可以使用UUID之类的东西(另一个答案提到)。这在很大程度上取决于返回值和调用序列的外观
作为一个例子,我在项目中实现了这个想法来处理这个问题。在RMIO库中,您只需处理“当前”操作被重复的可能性,因此您只需跟踪最后一个计数器值。如果您选择滚动您自己的伪事务系统,以下是基本操作: 在服务器端: