Я занимаюсь разработкой небольшого веб-приложения, сервлеты которого периодически получают доступ к общему ресурсу, который представляет собой простой текстовый файл на стороне сервера, содержащий несколько строк изменяемых данных. В большинстве случаев серветы просто читают файл для данных, но некоторые серветы также могут обновлять его, добавляя новые строки в файл или удаляя и заменяя существующие строки. Хотя содержимое файла обновляется не очень часто, вероятность несогласованности данных и повреждения файла по-прежнему мала, если два или более сервлета решают одновременно считывать и записывать в файл.
Первая цель - сделать чтение / запись файла безопасным . Для этой цели я создал вспомогательный класс FileReaderWriter, предоставляющий некоторые статические методы для поточно-ориентированного доступа к файлам. Методы чтения и записи координируются ReentrantReadWiteLock
. Правило довольно простое: несколько потоков могут читать из файла в любое время, если никакой другой поток не пишет в него одновременно.
public class FileReaderWriter {
private static final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
public static List<String> read(Path path) {
List<String> list = new ArrayList<>();
rwLock.readLock().lock();
try {
list = Files.readAllLines(path);
} catch (IOException e) {
e.printStackTrace();
} finally {
rwLock.readLock().unlock();
}
return list;
}
public static void write(Path path, List<String> list) {
rwLock.writeLock().lock();
try {
Files.write(path, list);
} catch (IOException e) {
e.printStackTrace();
} finally {
rwLock.writeLock().unlock();
}
}
}
Затем каждый сервелт может использовать вышеуказанный метод для файлачитаем так:
String dataDir = getServletContext().getInitParameter("data-directory");
Path filePath = Paths.get(dataDir, "test.txt");
ArrayList<String> list = FileReaderWriter.read(filePath);
Аналогично, запись может быть выполнена методом FileReaderWriter.write(filePath, list)
. Примечание: если некоторые данные необходимо заменить или удалить (что означает выборку данных из файла, обработку их и запись обновленных данных обратно в файл), то все пути кода для этой операции должны быть заблокированы rwLock.writeLock()
для атомарностипричины.
Теперь, когда доступ к общему файлу кажется безопасным (по крайней мере, я на это надеюсь), следующий шаг - сделать его быстрым . С точки зрения масштабируемости, чтение файла по запросу каждого пользователя сервлету не звучит разумно. Итак, я подумал о том, чтобы прочитать содержимое файла в ArrayList
(или другую коллекцию) только один раз за время инициализации контекста, а затем поделиться этим ArrayList (а не файлом) в качестве атрибута держателя контекста в области видимости. Затем атрибут контекстной области может совместно использоваться сервлетами с тем же механизмом блокировки, как описано выше, и содержимое обновленного ArrayList может независимо сохраняться обратно в файл на некоторой регулярной основе.
Другим решением (во избежание блокировки) будет использование CopyOnWriteArrayList
(или некоторого другого набора из пакета java.util.concurrent
) для хранения общих данных и назначение однопоточного ExecutorService
для выгрузки егосодержимое в файл, когда это необходимо. Я также слышал о Java-отображаемых файлах для отображения всего файла во внутреннюю память, но не уверен, подходит ли такой подход для данной конкретной ситуации.
Итак, кто-нибудь может, пожалуйста, подробно рассказать мне о наиболее эффективных способах (возможно, предложить некоторые другие альтернативы) для решения проблемы с доступом к файлу общего доступа, при условии, что запись в файл является довольно редкой иего содержание не должно превышать десятков строк.