Сбой JVM из-за блокировки файла nfs после отключения сети

Следующий фрагмент кода вызывает сбой 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) и утилитой оболочки flock(1).

Любые предложения/обходные пути?

PS: использование Oracle JDK 1.7.0_25 с NFSv4

EDIT: эта блокировка будет использоваться для определения того, какой из процессов активен в распределенном кластере высокой доступности. Код выхода 137. Что я ожидаю? способ обнаружения проблемы. закройте файл и попробуйте повторно получить.


person Amit G    schedule 18.09.2013    source источник
comment
Я просто скажу, что если вы развертываете приложение, которое каким-либо образом использует сетевые файловые системы, вы совершаете серьезную ошибку.   -  person user207421    schedule 18.09.2013


Ответы (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, как было предложено ранее.
person Dan Kruchinin    schedule 17.12.2013

Код выхода 138 НЕ намекает на SIGKILL — это сигнал 10, который может быть SIGBUS (в Solaris) или SIGUSR1 (в Linux). К сожалению, вы не говорите нам, какой из них вы используете.

По идее nfs должен все прозрачно обрабатывать - машина зависает, перезагружается, снимает блокировки. На практике я никогда не видел, чтобы это хорошо работало в NFS3, а NFS4 (которую вы используете) еще больше усложняет задачу, так как нет отдельных lockd() и statd().

Я бы порекомендовал вам запустить truss (solaris) или strace (linux) в вашем java-процессе, а затем вытащить сетевой штекер, чтобы узнать, что на самом деле происходит. Но, честно говоря, блокировка файловых систем NFS — это то, от чего люди не рекомендуют пока я использую Unix (уже более 25 лет), и я настоятельно рекомендую вам написать небольшую серверную программу, которая обрабатывает " кто чем занимается. Позвольте вашим клиентам подключаться к серверу, позвольте им отправить на сервер сообщение «начиная с X» и «остановившись, чтобы сделать X», и пусть сервер изящно отключит соединение, если клиент не отвечает более, чем, скажем, 5 минут. Я на 99% уверен, что это займет у вас меньше времени, чем попытка исправить блокировку NFS.

person Guntram Blohm    schedule 12.12.2013
comment
Ранее он сказал 138, а затем отредактировал вопрос на 137. В любом случае, мой ответ не зависит от номера сигнала. - person Guntram Blohm; 14.12.2013
comment
Еще одна идея, которая у меня только что возникла: есть ли в ваших системах скрипт cron или что-то подобное, который должен обрабатывать, когда сервер NFS выходит из строя и снова включается? Что-то вроде if test ! -f /mounted/file/system/lost+found; do fuser -km /mounted/file/system; umount -f /mounted/file/system/; mount /mounted/file/system; fi ? Linux не всегда хорошо обрабатывает перезагруженные серверы NFS (отказано в доступе к общему ресурсу NFS после его восстановления), поэтому, возможно, ваш системный администратор установил что-то подобное для решения проблемы. - person Guntram Blohm; 16.12.2013

Это поведение воспроизводится системным вызовом flock(2) и утилитой оболочки flock(1).

Поскольку вы можете воспроизвести его вне Java, это звучит как проблема с инфраструктурой. Вы не предоставили слишком много информации о вашем сервере NFS или клиентской ОС, но одна вещь, которую я видел, вызывает странное поведение с NFS, - это неправильная конфигурация DNS.

Убедитесь, что выходные данные от «uname -n» и «hostname» на клиенте соответствуют вашим записям DNS. Убедитесь, что сервер NFS правильно разрешает DNS.

Как и Гунтрам, я тоже не советую использовать 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();  
    }   
} 
person John R    schedule 17.12.2013