Как заблокировать файл на разных уровнях приложения? - PullRequest
6 голосов
/ 27 января 2009

Вот сценарий: у меня есть многопоточное Java-приложение, которое работает внутри контейнера сервлета. Приложение развертывается несколько раз внутри контейнера сервлета. Есть несколько контейнеров сервлетов работает на разных серверах.

Возможно, этот график проясняет:

server1
+- servlet container
   +- application1
   |  +- thread1
   |  +- thread2
   +- application2
      +- thread1
      +- thread2
server2
+- servlet container
   +- application1
   |  +- thread1
   |  +- thread2
   +- application2
      +- thread1
      +- thread2

В общей сетевой папке есть файл, к которому могут обращаться все эти потоки. И они часто обращаются к файлу. В большинстве случаев файл читается только этими потоками. Но иногда это пишется.

Мне нужно отказоустойчивое решение для синхронизации всех этих потоков, чтобы обеспечить согласованность данных.


Решения, которые не работают (правильно) :

  1. Использование java.nio.channels.FileLock
    Я могу синхронизировать потоки с разных серверов, используя класс FileLock. Но это не работает для потоков внутри того же процесса (контейнер сервлетов), поскольку блокировки файлов доступны для всего процесса.

  2. Использование отдельного файла для синхронизации
    Я мог бы создать отдельный файл, который указывает, что процесс читает или записывает файл. Это решение работает для всех потоков, но имеет несколько недостатков:

    • Производительность. Создание, удаление и проверка файлов являются довольно медленными операциями. Реализации с низким весом с одним файлом синхронизации предотвратят параллельное чтение файла.
    • Файл синхронизации останется после сбоя JVM, что потребует ручной очистки.
    • У нас уже были странные проблемы с удалением файлов в сетевых файловых системах.
  3. Использование обмена сообщениями
    Мы могли бы реализовать систему обмена сообщениями, которую потоки использовали бы для координации доступа к файлу. Но это кажется слишком сложным для этой проблемы. И снова: производительность будет плохой.

Есть мысли?

Ответы [ 6 ]

2 голосов
/ 28 января 2009

Самое простое решение - это создать другой процесс (веб-сервис или что-то простое для вас). Только этот процесс читает / записывает файл и прослушивает запросы на чтение / запись от других служб.

Хотя может показаться, что это медленнее, чем использование сетевого ресурса напрямую, это не обязательно так: использование сетевого ресурса означает использование клиента / сервера, встроенного в вашу ОС (что и делает: отправьте чтение / написать запросы на сервер, который предлагает долю).

Поскольку ваш сервис оптимизирован для этой задачи (вместо того, чтобы быть общим сервисом "подачи файлов"), он может быть даже быстрее.

2 голосов
/ 27 января 2009

A. Похоже, пришло время для базы данных :-). Вместо того, чтобы иметь общий файл, как насчет хранения данных в базе данных.

B. Альтернативно - наслоение:

  1. Блокировка потоков внутри процесса с помощью стандартной синхронизированной блокировки.
  2. Блокировка межпроцессного / компьютера с помощью типа блокировки на основе файла - например, создать каталог для блокировки.

Гнездо 2 внутри 1.

По-прежнему существует проблема очистки.

C. В качестве альтернативы какая-то стратегия записи в новый файл / переименование, чтобы читателю не нужно было блокировать, может быть?

2 голосов
/ 27 января 2009

Вы перечислили возможные решения, кроме очевидного: удалите зависимость от этого файла

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

1 голос
/ 28 января 2009

Если вам нужно писать файл редко, как насчет записи файла под временным именем и последующего использования переименования, чтобы сделать его «видимым» для читателей?

Это работает надежно только с файловыми системами Unix. В Windows вам нужно будет обработать случай, когда какой-то процесс открывает файл (для чтения). В этом случае переименовать не удастся. Просто повторите попытку, пока переименование не пройдет успешно.

Я предлагаю тщательно проверить это, потому что вы можете столкнуться с перегрузкой: существует так много запросов на чтение, что задача записи не может заменить файл в течение длительного времени.

Если это так, заставьте читателей проверить временный файл и подождите несколько секунд со следующим чтением, пока файл не исчезнет.

1 голос
/ 27 января 2009

Сможете ли вы использовать Семафор для управления доступом по одному в приложении?

Цитируя API «Семафоры часто используются для ограничения количества потоков, которые могут получить доступ к некоторым (физическим или логическим) ресурсам»

Хотя этот API может оставаться специфичным для контейнера, концепция распределенного семафора должна быть достижимой, возможно, с помощью JGroups .

Поиском в Google был найден «распределенный Java-семафор» Jukebox , который, похоже, мог бы справиться с вышеуказанным

1 голос
/ 27 января 2009

Использование java.nio.channels.FileLock с ReadWriteLock.

На вашем месте я бы скрыл File, FileChannel и весь FileOutputStream от всего бизнес-кода. Заменено на мой собственный простой класс адаптера, как DAO.

, например

abstract class MyWriter{
    private FileChannel file;
    public void writeSomething(byte[] b){
        // get VM scope write lock here
        // get file lock here
        // do write
        // release file lock
        // release readwritelock lock
    }
}
...