#clojure #io
#clojure #io
Вопрос:
Я работаю над сценарием, который должен считывать десятки тысяч файлов с диска. Я пытаюсь понять, как лучше всего это сделать. Я столкнулся с проблемой, когда я использую map
для этого два пакета clj-glob
и clojure-mail
:
(def sent-mail-paths
(->> (str maildir-path "/*/_sent_mail/*")
(glob) ;; returns files using clojure.java.io/as-file
(map str) ;; i just want the paths
))
(def msgs
(->> sent-mail-paths ;; 30K paths
(map mail/file->message)))
откуда glob
берется функция в первом блоке clj-glob
и используется as-file
для возврата набора файловых объектов (см. Здесь ). Мне нужны только строки пути, поэтому я это делаю (map str)
. mail/file->message
Функция во втором блоке использует with-open
вместе с FileInputStream
классом Java для чтения файлов (см. Здесь ).
Проблема, с которой я сталкиваюсь, заключается в том, что этот код вызывает ошибку в тот момент, когда я пытаюсь обработать файлы в результирующей отложенной последовательности, выполнив даже что-то вроде:
(count msgs)
Ошибка:
(Слишком много открытых файлов в системе)
Единственный способ, которым я смог выполнить эту работу, — использовать doseq
:
(def msgs (->> list-of-paths ;; 30K paths
(map mail/file->message)))
(def final (atom []))
(doseq [x result]
(swap! final conj (mail/file->message x)))
Мой вопрос в том, является ли это лучшим (единственным?) как выполнить этот процесс, не открывая тысячи и тысячи файлов одновременно? Я не совсем понимаю, почему я не могу использовать отложенную последовательность, которая возвращается map
. Почему это приводит к открытию тонны файлов.
Кстати, я заметил одну вещь clj-glob
, которая не является хорошо обслуживаемым пакетом, не используется with-open
при вызове as-file
…
Комментарии:
1. Извините, не могли бы вы опубликовать свой фактический код для процесса? Отложенная оценка — это не асинхронность или параллелизм, элементы обрабатываются один за другим, подход должен работать, если у вас нет утечки дескриптора.
2. У людей возникают проблемы с отложенными разделами и файлами, когда они возвращают отложенную последовательность содержимого файла — если вы не будете осторожны, вы можете закрыть файл до его завершения или полностью удалить файл. Это не то:-)
3. Наконец, ваша последняя часть действительно дословна? Вы определяете результаты, а затем используете результат. Вы также определяете результаты с помощью процесса map, а затем позже снова вызываете process … Спасибо!
4. Почему бы и нет
(map process list-of-paths)
? Чего мне не хватает? Если вам нужен параллелизм, вы можете использовать(pmap process list-of-paths)
…5. Хммм, я обновлю вопрос с помощью некоторого кода. Я думал, что понял проблему с точки зрения того, почему это не сработало, но, похоже, это не так. Я определенно могу обойтись без последовательного доступа, и на самом деле это то, что я пытаюсь сделать.
Ответ №1:
Даже если вы открываете / закрываете файлы правильно, есть вероятность, что во время выполнения программы вы достигнете внутренне определенного ограничения на количество файловых дескрипторов, которые может иметь ваша программа (это часто встречается в программах с долгим сроком службы, таких как микросервисы).
Вы можете прочитать здесь о том, как посмотреть, каков этот предел в настоящее время и как его увеличить: https://www.cyberciti.biz/faq/linux-increase-the-maximum-number-of-open-files /
Комментарии:
1. Открытие и закрытие файла не зависит от дескриптора файла. К этому приведет только одновременное открытие большого количества файлов (или сокетов) или их утечка.
2. Я столкнулся с такой ситуацией, когда служба принимала такой объем запросов, что это могло привести к открытию большого количества файлов одновременно, прежде чем все файлы могли быть закрыты, что привело к превышению предела.