我们有一个java进程,它使用apachecommons-vfs侦听文件系统上的目录X。每当一个新文件被导出到此目录时,我们的过程就会开始。我们首先将文件重命名为filename.处理并解析文件名,从文件中获取一些信息并插入到表中,然后将此文件发送到文档管理系统。这是每个集群的单线程应用程序。现在考虑一下在集群环境中运行,我们有5个服务器。因此,5个不同的虚拟机正在尝试访问同一个文件。整个实现的基础是,在给定的时间只有一个进程可以将文件重命名为.producing,因为操作系统不允许多个进程同时修改文件。一旦一个集群持有并将文件重命名为.processing,其他集群将忽略格式为.processing.的文件
一年多以来,这项工作一直很好,但刚才我们发现很少有重复项。看起来多个集群已经掌握了该文件,在这种情况下,假设集群a、b、c已经访问了文件f.pdf,他们同时将其重命名为f.pdf.processing(我仍然困惑于操作系统如何允许同时修改文件)。结果,集群a、b、c处理了文件并将其发送到文档管理系统。所以现在有3个重复的文件。
简而言之,我所研究的是在集群环境中只运行一次任务的方法。我还希望它有一个故障转移机制,这样,如果集群出现问题,另一个集群就会承担任务。我们不想在盒子上设置env变量,比如master=true,因为这会将其限制为仅一个集群,并且不会处理故障转移。
任何形式的帮助都将不胜感激。
请参阅以下关于文件锁定的文章:文件系统如何处理并发读/写?
文件的读写操作(包括重命名)不是原子操作,进程之间也没有很好地同步,正如您所假设的那样——至少在大多数操作系统上不是这样。
但是,创建一个新文件通常是一个原子操作。你可以利用这个优势。这个概念被称为整个文件锁定。
我们正在使用应用程序数据库中的共享锁表来实现我们自己的同步逻辑。这允许所有集群节点在实际启动作业之前检查作业是否已经在运行。
在将文件重命名为.producing之前,是否尝试使用FileLock tryLock()或lock()?如果你没有,我认为你应该尝试,所以在这种情况下,只有一个应用程序可以更改这个文件。
更新:对不起,我忘了你问VDF的事了。在ApacheVDF中(事实上,在ApacheSynapse中),我发现了VFSUtils类,它有以下方法:
public static boolean acquireLock(org.apache.commons.vfs2.FileSystemManager fsManager,
org.apache.commons.vfs2.FileObject fo)
Acquires a file item lock before processing the item, guaranteing that the file is not processed while it is being uploaded and/or the item is not processed by two listeners
Parameters:
fsManager - used to resolve the processing file
fo - representing the processign file item
Returns:
boolean true if the lock has been acquired or false if not
我认为,这种方法可以解决您的问题(如果您可以在项目中使用ApacheSynapse)。