Чтение новых данных из постоянно обновляемого файла в Java

У меня есть файл журнала, который постоянно пополняется новыми строками данных. Мне нужно получить новые добавленные данные в java, как только они будут написаны. На данный момент мое решение:

public static void readNonStop(String filename, boolean goToEnd, FileReadCallback readCallback) {
    if (readCallback == null) {
        return;
    }
    try {
        BufferedReader br = new BufferedReader(new FileReader(filename));
        try {
            String line = br.readLine();
            int lineNumber = 0;

            if (goToEnd) {
                while(br.readLine() != null) {}
            }

            while (true) {
                if (line != null) {
                    readCallback.onRead(lineNumber++, line);
                } else {
                    Thread.sleep(1);
                }
                line = br.readLine();
            }
        } finally {
            br.close();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

Но я чувствую, что должен быть способ получше. Мне не нравится идея постоянного цикла выполнения со «сном» внутри, и я бы предпочел какой-то подход, управляемый событиями.

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

Как правильно это сделать в данной ситуации?

Заранее спасибо!

В настоящее время у вас нет конкретного вопроса. Таким образом, ваш пост будет лучше размещен на Проверка кода

hotzst 27.12.2018 19:49

Вы смотрели Java Watch Service. Это лучше, чем пытаться реализовать свою собственную логику. Взгляните на эту публикацию - stackoverflow.com/questions/16251273/…

Dinesh 27.12.2018 20:00

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

Arthur 27.12.2018 20:26

Вы пробовали Apache Commons TailerListener? Документация Javadoc: commons.apache.org/proper/commons-io/apidocs/org/apache/comm ons /… Реализация: commons.apache.org/proper/commons-io/apidocs/src-html/org/…

JuanMoreno 27.12.2018 20:55
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
4
139
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Файлы не предназначены для обмена сообщениями. Даже при использовании TCP по шлейфу задержка может составлять 10–30 микросекунд. Без изменения формата файла ваше решение, вероятно, будет самым быстрым.

ПРИМЕЧАНИЕ: вам не нужно спать целую миллисекунду. Вы можете использовать Thread.yield() или LockSupport.parkNanos(100_000);. Для более сложной стратегии вы можете иметь такой класс, как LongPauser, который отключается настраиваемым образом.

Кстати, я реализовал решение для записи / чтения файлов с низкой задержкой, которое называется Chronicle Queue. У этого есть субмикросекундные задержки с использованием двоичного формата для скорости.

ПРИМЕЧАНИЕ. Можно просто до конца, пропустив все байты available(), когда вы открываете файл как FileInputStream. Это может привести к неполной строке в зависимости от того, как работает ваша буферизация.

Спасибо за Ваш ответ. В настоящее время единственное, что мне не нравится в этом решении, - это нагрузка на ЦП, если нет "Thread.sleep". По крайней мере, при 1 миллисекунде сна загрузка ЦП снижается с 40% до 0-1%. Использование WatchService в некоторых случаях приводит к задержке до 5-6 секунд, что недопустимо. Дело в том, что это единственный способ получить необходимую информацию, поэтому у меня нет возможности переключать способы связи. Вместо этого я должен использовать FileInputStream.

Arthur 27.12.2018 20:59

Вы можете поспать дольше 10 мс. LongPauser спит все дольше и дольше, пока вы не вызовете reset (). Это сводит к минимуму задержку, когда вещи заняты.

Peter Lawrey 27.12.2018 21:08

На Code Review я получил предложение использовать класс Tailer из сообщества Apache. Глядя на его код, похоже, я понял правильную идею. Хотя Tailer немного более надежен, он использует ту же логику, что и мой код, он читает в цикле с задержкой Thread.sleep.

Arthur 27.12.2018 21:45
Файлы не предназначены для обмена сообщениями. Действительно. Это очень плохой дизайн, особенно если в качестве инструмента используется Java. Чтение изменяющегося / растущего файла достаточно сложно, чтобы сделать его надежным с помощью низкоуровневого кода C. С Java это де-факто невозможно, потому что вы не можете контролировать, например, кэширует ли JVM размер файла.
Andrew Henle 28.12.2018 09:26

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

Arthur 28.12.2018 10:58

Другие вопросы по теме