6

以下代码段导致JVM崩溃:如果获取锁后发生网络中断

    while (true) {

       //file shared over nfs
       String filename = "/home/amit/mount/lock/aLock.txt";
       RandomAccessFile file = new RandomAccessFile(filename, "rws");
       System.out.println("file opened");
       FileLock fileLock = file.getChannel().tryLock();
       if (fileLock != null) {
          System.out.println("lock acquired");
       } else {
          System.out.println("lock not acquired");
       }

       try {
          //wait for 15 sec
          Thread.sleep(30000);
       } catch (InterruptedException e) {
          e.printStackTrace();
       }
       System.out.println("closing filelock");
       fileLock.close();
       System.out.println("closing file");
       file.close();
    }

观察:JVM 收到 KILL(9) 信号并以退出代码 137(128+9) 退出。

可能在重新建立网络连接后,文件描述符表中出现了问题。这种行为可以通过系统调用flock(2) 和shell 实用程序flock(1) 重现。

有什么建议/解决方法吗?

PS:使用带有 NFSv4 的 Oracle JDK 1.7.0_25

编辑:此锁将用于识别分布式高可用性集群中哪个进程处于活动状态。退出代码是 137。我期望什么?检测问题的方法。关闭文件并尝试重新获取。

4

3 回答 3

5

退出代码 138 不会提示 SIGKILL - 这是信号 10,它可以是 SIGBUS(在 solaris 上)或 SIGUSR1(在 linux 上)。不幸的是,您没有告诉我们您使用的是哪一个。

理论上,nfs 应该透明地处理所有事情——机器崩溃、重启和清除锁。在实践中,我从未见过 NFS3 能很好地工作,而 NFS4(您正在使用的)使事情变得更加困难,因为没有单独的 lockd() 和 statd()。

我建议你在你的 java 进程上运行 truss(solaris) 或 strace (linux),然后拔掉网络插件,看看到底发生了什么。但老实说,只要我使用 Unix(到现在已经超过 25 年),人们就建议不要锁定 NFS 文件系统,我强烈建议您编写一个小型服务器程序来处理“谁做什么”的事情。让您的客户端连接到服务器,让他们向服务器发送一些“以 X 开始”和“停止执行 X”的消息,如果客户端不回答超过,例如,让服务器优雅地超时连接, 5分钟。我有 99% 的把握与尝试修复 NFS 锁定相比,这将花费您更少的时间。

于 2013-12-12T14:53:19.547 回答
3

NFS 服务器重新启动后,所有具有任何活动文件锁的客户端都会启动锁回收过程,该过程持续时间不超过所谓的“宽限期”(只是一个常数)。如果在宽限期内回收过程失败,NFS 客户端(通常是内核空间野兽)将 SIGUSR1 发送到无法恢复其锁的进程。这就是你问题的根源。

当服务器端锁定成功时,客户端系统上的 rpc.lockd 请求另一个守护进程 rpc.statd 来监视实现锁定的 NFS 服务器。如果服务器发生故障然后恢复,rpc.statd 将被通知。然后它会尝试重新建立所有活动锁。如果 NFS 服务器发生故障并恢复,并且 rpc.lockd 无法重新建立锁,它会向请求锁的进程发送一个信号 (SIGUSR1)。

http://menehune.opt.wfu.edu/Kokua/More_SGI/007-2478-010/sgi_html/ch07.html

您可能想知道如何避免这种情况。好吧,有几种方法,但没有一个是理想的:

  1. 增加宽限期。AFAIR,在 linux 上可以通过 /proc/fs/nfsd/nfsv4leasetime 进行更改。
  2. 在您的代码中创建一个 SIGUSR1 处理程序并在那里做一些聪明的事情。例如,在信号处理程序中,您可以设置一个标志,表示锁恢复失败。如果设置了此标志,您的程序可以尝试等待 NFS 服务器准备就绪(只要它需要),然后它可以尝试自行恢复锁。不是很有收获...
  3. 不要再使用 NFS 锁定。如果可以按照前面的建议切换到 zookeeper。
于 2013-12-17T22:40:53.300 回答
1

这种行为可以通过系统调用flock(2) 和shell 实用程序flock(1) 重现。

由于您能够在 Java 之外重现它,这听起来像是一个基础设施问题。您没有提供太多有关您的 NFS 服务器或客户端操作系统的信息,但我看到导致 NFS 出现奇怪行为的一件事是 DNS 配置不正确。

检查客户端上“uname -n”和“主机名”的输出是否与您的 DNS 记录匹配。检查 NFS 服务器是否正确解析 DNS。

像 Guntram 一样,我也建议不要将 NFS 用于此类事情。我会使用Hazlecast(无服务器,实例动态集群)或ZooKeeper(需要设置服务器)。

使用 Hazlecast,您可以执行此操作来获取集群范围内的独占锁:

import com.hazelcast.core.Hazelcast;
import java.util.concurrent.locks.Lock;

Lock lock = Hazelcast.getLock(myLockedObject);
lock.lock();
try {
    // do something here
} finally {
    lock.unlock();
} 

它还支持超时:

if (lock.tryLock (5000, TimeUnit.MILLISECONDS)) {
    try {  
       // do some stuff here..  
   } 
    finally {  
      lock.unlock();  
    }   
} 
于 2013-12-17T21:23:05.010 回答