Groovy / Java: параллельная обработка структуры каталогов, где каждый узел представляет собой список подкаталогов / файлов - PullRequest
0 голосов
/ 03 октября 2019

Вот моя текущая проблема:

У меня есть структура каталогов, хранящаяся где-то в облачном хранилище. В папке Root у меня есть 1000+ подкаталогов, и у каждого из них есть один подкаталог под ними. И внутри каждого из этих подкаталогов существует один файл. Таким образом, упрощенная диаграмма выглядит примерно так:

                      Root
       ________________|________________
      |         |             |         |
   FolderA   FolderB  ...  FolderY   FolderZ
      |         |             |         |
   Folder1   Folder2       Folder3   Folder4
      |         |             |         |
    FileA     FileB         FileC     FileD

Для каждого узла у него есть свойства type ("каталог" или "файл") и path ("/ Root / FolderB"). И единственный способ получить эти узлы - вызвать метод с именем listDirectory(path), который отправляется в облако, получает все объекты в этом path. Мне нужно найти все файлы и обработать их.

Проблема заключается в том, что при том, как он структурирован, если я хочу найти FileA, мне нужно три раза 1016 * позвонить* (Root -> FolderA -> Folder1), который вы можете себе представить, значительно замедляет все это.

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

К вашему сведению, я не могу изменить метод listDirectory(). Каждый вызов извлекает все объекты в этом пути.

TL; DR: Мне нужно найти параллельный способ обработки через файловую структуру облачного хранилища, где единственный способ получитьпапки / файлы создаются методом listDirectory(path).

1 Ответ

0 голосов
/ 12 октября 2019

Если кэширование структуры каталогов в памяти с помощью deamon не является вариантом.

или кэширование структуры каталогов путем первоначального создания одноразового сопоставления структуры хранения в памяти и подключения к каждому удалению addОперация обновления для хранилища и соответствующая смена базы данных не возможны.

при условии, что структура хранилища является древовидной (обычно таковой), потому что работает listDirectory(), я думаю, вам лучше использовать Breadthпервый поиск для поиска в дереве структуры хранения. таким образом, вы можете искать один уровень за раз с помощью параллельного программирования

ваш код может выглядеть примерно так:

SearchElement.java - представляет собой каталог или файл

public class SearchElement {

private String path;
private String name;

public SearchElement(String path, String name) {
    this.path = path;
    this.name = name;
}

public String getPath() {
    return path;
}

public String getName() {
    return name;
}

}

ElementFinder.java - класс, который ищет хранилище, необходимое для замены функции listDirectory в вашей реализации

import java.util.ArrayList;
import java.util.Collection;
import java.util.Optional;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicReference;

public class ElementFinder {
    private final SearchElement ROOT_DIRECTORY_PATH = new SearchElement("/", "");


    public Optional<SearchElement> find(String elementName) {
        Queue<SearchElement> currentLevelElements = new ConcurrentLinkedQueue();
        currentLevelElements.add(ROOT_DIRECTORY_PATH);

        AtomicReference<Optional<SearchElement>> wantedElement = new AtomicReference<>(Optional.empty());

        while (!currentLevelElements.isEmpty() && wantedElement.get().isEmpty()) {
            Queue<SearchElement> nextLevelElements = new ConcurrentLinkedQueue();
            currentLevelElements.parallelStream().forEach(currentSearchElement -> {
                Collection<SearchElement> subDirectoriesAndFiles = listDirectory(currentSearchElement.getPath());

                subDirectoriesAndFiles.stream()
                        .filter(searchElement -> searchElement.getName().equals(elementName))
                        .findAny()
                        .ifPresent(element -> wantedElement.set(Optional.of(element)));

                nextLevelElements.addAll(subDirectoriesAndFiles);
            });

            currentLevelElements = nextLevelElements;
        }

        return wantedElement.get();
    }

    private Collection<SearchElement> listDirectory(String path) {
        return new ArrayList<>(); // replace me!
    }
}
...