All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.qiniu.datasource.CloudStorageContainer Maven / Gradle / Ivy

package com.qiniu.datasource;

import com.google.gson.JsonObject;
import com.qiniu.common.QiniuException;
import com.qiniu.common.SuitsException;
import com.qiniu.interfaces.IDataSource;
import com.qiniu.interfaces.ILineProcess;
import com.qiniu.interfaces.IStorageLister;
import com.qiniu.interfaces.ITypeConvert;
import com.qiniu.interfaces.IResultOutput;
import com.qiniu.util.*;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public abstract class CloudStorageContainer extends DatasourceActor implements IDataSource, IResultOutput, T> {

    protected String bucket;
    protected List antiPrefixes;
    protected boolean hasAntiPrefixes = false;
    protected ConcurrentMap> prefixesMap;
    protected List prefixes;
    protected boolean prefixLeft;
    protected boolean prefixRight;
    protected ILineProcess processor; // 定义的资源处理器
    protected List originPrefixList;
    static String firstPoint;
    private String lastPoint;
    private ConcurrentMap> prefixAndEndedMap = new ConcurrentHashMap<>(100);

    public CloudStorageContainer(String bucket, Map> prefixesMap, List antiPrefixes,
                                 boolean prefixLeft, boolean prefixRight, Map indexMap, List fields,
                                 int unitLen, int threads) throws IOException {
        super(unitLen, threads);
        this.bucket = bucket;
        this.prefixLeft = prefixLeft;
        this.prefixRight = prefixRight;
        // 先设置 antiPrefixes 后再设置 prefixes,因为可能需要从 prefixes 中去除 antiPrefixes 含有的元素
        setAntiPrefixes(antiPrefixes);
        setPrefixesAndMap(prefixesMap);
        setIndexMapWithDefault(indexMap);
        if (fields != null && fields.size() > 0) this.fields = fields;
        else this.fields = ConvertingUtils.getOrderedFields(this.indexMap, null);
        // default save parameters,默认全记录保存
        setSaveOptions(true, "result", "tab", "\t", null);
        // 由于目前指定包含 "|" 字符的前缀列举会导致超时,因此先将该字符及其 ASCII 顺序之前的 "{" 和之后的("|}~")统一去掉,从而优化列举的超
        // 时问题,简化前缀参数的设置,也避免为了兼容该字符去修改代码算法
        originPrefixList = Arrays.asList(
                (" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz").split("")
        );
        firstPoint = originPrefixList.get(0);
        lastPoint = originPrefixList.get(originPrefixList.size() - 1);
    }

    private void setAntiPrefixes(List antiPrefixes) {
        if (antiPrefixes != null && antiPrefixes.size() > 0) {
            hasAntiPrefixes = true;
            this.antiPrefixes = antiPrefixes.stream().sorted().collect(Collectors.toList());
            int size = this.antiPrefixes.size();
            Iterator iterator = this.antiPrefixes.iterator();
            String temp = iterator.next();
            while (iterator.hasNext() && size > 0) {
                size--;
                String prefix = iterator.next();
                if (prefix.startsWith(temp)) iterator.remove();
                else temp = prefix;
            }
        }
    }

    private void setPrefixesAndMap(Map> prefixesMap) throws IOException {
        if (prefixesMap == null || prefixesMap.size() <= 0) {
            this.prefixesMap = new ConcurrentHashMap<>(threads);
            prefixLeft = true;
            prefixRight = true;
            if (hasAntiPrefixes && !"upyun".equals(getSourceName())) prefixes = originPrefixList;
        } else {
            if (prefixesMap.containsKey(null) || prefixesMap.containsValue(null)) throw new IOException("prefixes map can not contain null.");
            this.prefixesMap = new ConcurrentHashMap<>(threads);
            this.prefixesMap.putAll(prefixesMap);
            prefixes = prefixesMap.keySet().stream().sorted().distinct().collect(Collectors.toList());
            int size = prefixes.size();
            Iterator iterator = prefixes.iterator();
            String temp = iterator.next();
            Map value = prefixesMap.get(temp);
            String start = null;
            String end = null;
            String marker = null;
            if (temp.equals("") && !iterator.hasNext()) {
                if (value != null && value.size() > 0) {
                    start = "".equals(value.get("start")) ? null : value.get("start");
                    end = "".equals(value.get("end")) ? null : value.get("end");
                    marker = "".equals(value.get("marker")) ? null : value.get("marker");
                }
                if (start == null && end == null && marker == null) throw new IOException("prefixes can not only be empty string(\"\")");
            } else {
                end = value == null ? null : value.get("end");
            }
            while (iterator.hasNext() && size > 0) {
                size--;
                String prefix = iterator.next();
                if (prefix.startsWith(temp)) {
                    if (end == null || "".equals(end)) {
                        iterator.remove();
                        this.prefixesMap.remove(prefix);
                    } else if (end.compareTo(prefix) >= 0) {
                        throw new IOException(temp + "'s end can not be larger than " + prefix + " in " + prefixesMap);
                    }
                } else {
                    temp = prefix;
                    value = prefixesMap.get(temp);
                    end = value == null ? null : value.get("end");
                }
            }
        }
        if (hasAntiPrefixes && prefixes != null && prefixes.size() > 0) {
            antiPrefixes.sort(Comparator.naturalOrder());
            if (firstPoint.compareTo(antiPrefixes.get(0)) > 0) {
                throw new IOException("min anti-prefix can not be smaller than prefix: " + firstPoint);
            }
            String firstPrefix = prefixes.get(0);
            if (prefixLeft && firstPrefix.compareTo(antiPrefixes.get(0)) >= 0) {
                throw new IOException("with prefix-left, min anti-prefix can not be smaller than min prefix: " + firstPrefix);
            }
            String lastAntiPrefix = antiPrefixes.get(antiPrefixes.size() - 1);
            if (lastPoint.compareTo(lastAntiPrefix) < 0) {
                throw new IOException("max anti-prefix can not be larger than prefix: " + lastPoint);
            }
            String lastPrefix = prefixes.get(prefixes.size() - 1);
            if (prefixRight && lastPrefix.compareTo(lastAntiPrefix) <= 0) {
                throw new IOException("with prefix-right, max anti-prefix can not be same as or larger than max prefix: " + lastPoint);
            }
        }
    }

    private void setIndexMapWithDefault(Map indexMap) throws IOException {
        if (indexMap == null || indexMap.size() == 0) {
            if (this.indexMap == null) this.indexMap = new HashMap<>();
            for (String fileInfoField : ConvertingUtils.defaultFileFields) {
                this.indexMap.put(fileInfoField, fileInfoField);
            }
        } else {
            for (String s : indexMap.keySet()) {
                if (s == null || "".equals(s)) throw new IOException("the index can not be empty in " + indexMap);
            }
            this.indexMap = indexMap;
        }
    }

    public void setProcessor(ILineProcess processor) {
        this.processor = processor;
    }

    /**
     * 检验 prefix 是否有效,在 antiPrefixes 前缀列表中或者为空均无效
     * @param prefix 待检验的 prefix
     * @return 检验结果,true 表示 prefix 有效不需要剔除
     */
    boolean checkPrefix(String prefix) {
//        if (prefix == null) return false;
//        if (hasAntiPrefixes) {
            for (String antiPrefix : antiPrefixes) {
                if (prefix.startsWith(antiPrefix)) return false;
            }
            return true;
//        } else {
//            return true;
//        }
    }

    protected abstract ITypeConvert getNewConverter();

    protected abstract ITypeConvert getNewStringConverter() throws IOException;

    /**
     * 执行列举操作,直到当前的 lister 列举结束,并使用 processor 对象执行处理过程
     * @param lister 已经初始化的 lister 对象
     * @param saver 用于列举结果持久化的文件对象
     * @param processor 用于资源处理的处理器对象
     * @throws IOException 列举出现错误或者持久化错误抛出的异常
     */
    public void export(IStorageLister lister, IResultOutput saver, ILineProcess processor) throws Exception {
        ITypeConvert converter = getNewConverter();
        ITypeConvert stringConverter = null;
        if (saveTotal) {
            stringConverter = getNewStringConverter();
            saver.preAddWriter("failed");
        }
        List convertedList;
        List writeList;
        List objects = lister.currents();
        boolean hasNext = lister.hasNext();
        int retry;
        Map map = prefixAndEndedMap.get(lister.getPrefix());
        JsonObject json = map != null ? JsonUtils.toJsonObject(map) : (hasNext ? new JsonObject() : null);
        // 初始化的 lister 包含首次列举的结果列表,需要先取出,后续向前列举时会更新其结果列表
        while (objects.size() > 0 || hasNext) {
            if (stopped) break;
            if (LocalDateTime.now(DatetimeUtils.clock_Default).isAfter(pauseDateTime)) {
                synchronized (object) {
                    object.wait();
                }
            }
            if (stringConverter != null) {
                writeList = stringConverter.convertToVList(objects);
                if (writeList.size() > 0) saver.writeSuccess(String.join("\n", writeList), false);
                if (stringConverter.errorSize() > 0) saver.writeToKey("failed", stringConverter.errorLines(), false);
            }
            if (processor != null) {
                convertedList = converter.convertToVList(objects);
                if (converter.errorSize() > 0) saver.writeError(converter.errorLines(), false);
                // 如果抛出异常需要检测下异常是否是可继续的异常,如果是程序可继续的异常,忽略当前异常保持数据源读取过程继续进行
                try {
                    processor.processLine(convertedList);
                } catch (QiniuException e) {
                    if (HttpRespUtils.checkException(e, 2) < -1) throw e;
                    errorLogger.error("process objects: {}", lister.getPrefix(), e);
                    if (e.response != null) e.response.close();
                }
            }
            if (hasNext) {
                json.addProperty("marker", lister.getMarker());
                recordLister(lister.getPrefix(), json.toString());
            }
            if (map != null) map.put("start", lister.currentEndKey());
            statistics.addAndGet(objects.size());
            if (stopped) break;
//            objects.clear(); 上次其实不能做 clear,会导致 lister 中的列表被清空
            retry = retryTimes;
            while (true) {
                try {
                    lister.listForward(); // 要求 listForward 实现中先做 hashNext 判断,if (!hasNext) 置空;
                    objects = lister.currents();
                    break;
                } catch (SuitsException e) {
                    retry = HttpRespUtils.listExceptionWithRetry(e, retry);
                    try {FileUtils.createIfNotExists(errorLogFile); } catch (IOException ignored) {}
                    errorLogger.error("list objects by prefix:{} retrying...", lister.getPrefix(), e);
                }
            }
            hasNext = lister.hasNext();
        }
    }

    protected abstract IResultOutput getNewResultSaver(String order) throws IOException;

    /**
     * 将 lister 对象放入线程池进行执行列举,如果 processor 不为空则同时执行 process 过程
     * @param lister 列举对象
     */
    void listing(IStorageLister lister) {
        // 持久化结果标识信息
        int order = UniOrderUtils.getOrder();
        String orderStr = String.valueOf(order);
        IResultOutput saver = null;
        ILineProcess lineProcessor = null;
        try {
            saver = getNewResultSaver(orderStr);
            saverMap.put(orderStr, saver);
            // 多线程情况下不要直接使用传入的 processor,因为对其关闭会造成 clone 的对象无法进行结果持久化的写入
            if (processor != null) {
                lineProcessor = processor.clone();
                lineProcessor.changeSaveOrder(orderStr);
                processorMap.put(orderStr, lineProcessor);
            }
            export(lister, saver, lineProcessor);
            procedureLogger.info("{}-|-", lister.getPrefix());
            progressMap.remove(lister.getPrefix()); // 只有 export 成功情况下才移除 record
        } catch (QiniuException e) {
            try { FileUtils.createIfNotExists(errorLogFile); } catch (IOException ignored) {}
            errorLogger.error("{}: {}, {}", lister.getPrefix(), progressMap.get(lister.getPrefix()), e.error(), e);
            if (e.response != null) e.response.close();
        } catch (Throwable e) {
            try { FileUtils.createIfNotExists(errorLogFile); } catch (IOException ignored) {}
            errorLogger.error("{}: {}", lister.getPrefix(), progressMap.get(lister.getPrefix()), e);
        } finally {
            try { FileUtils.createIfNotExists(infoLogFile); } catch (IOException ignored) {}
            infoLogger.info("{}\t{}\t{}", orderStr, lister.getPrefix(), lister.count());
            if (saver != null) {
                saver.closeWriters();
                saver = null; // let gc work
            }
            saverMap.remove(orderStr);
            processorMap.remove(orderStr);
            if (lineProcessor != null) {
                lineProcessor.closeResource();
                lineProcessor = null;
            }
            UniOrderUtils.returnOrder(order); // 最好执行完 close 再归还 order,避免上个文件描述符没有被使用,order 又被使用
            lister.close();
        }
    }

    protected abstract IStorageLister getLister(String prefix, String marker, String start, String end, int unitLen) throws SuitsException;

    IStorageLister generateLister(String prefix) throws SuitsException {
        return generateLister(prefix, 0);
    }

    private IStorageLister generateLister(String prefix, int limit) throws SuitsException {
        limit = limit > 0 ? limit : unitLen;
        int retry = retryTimes;
        Map map = prefixesMap.get(prefix);
        String marker;
        String start;
        String end;
        if (map == null) {
            marker = start = end = null;
        } else {
            marker = map.get("marker");
            start = map.get("start");
            end = map.get("end");
        }
        while (true) {
            try {
                return getLister(prefix, marker, start, end, limit);
            } catch (SuitsException e) {
                retry = HttpRespUtils.listExceptionWithRetry(e, retry);
                try { FileUtils.createIfNotExists(errorLogFile); } catch (IOException ignored) {}
                errorLogger.error("generate lister by prefix:{} retrying...", prefix, e);
            }
        }
    }

    private String nextPoint(IStorageLister lister, boolean doFutureCheck) {
        boolean next;
        try {
            next = doFutureCheck ? lister.hasFutureNext() : lister.hasNext();
        } catch (SuitsException e) {
            errorLogger.warn("check lister hasFutureNext of \"{}\" has error: ", lister.getPrefix(), e);
            next = lister.hasNext();
        }
        String startPrefix = lister.getPrefix();
        String point = null;
        if (next) {
            // 如果存在 next 且当前获取的最后一个对象文件名不为空,则可以根据最后一个对象的文件名计算后续的前缀字符
            String endKey = lister.currentEndKey();
            int prefixLen = startPrefix.length();
            if (endKey != null) {
                if (endKey.length() > prefixLen) {
                    // 如果最后一个对象的文件名长度大于 prefixLen,则可以取出从当前前缀开始的下一个字符 point,用于和预定义前缀列表进行比较,
                    // 确定 lister 的 endPrefix
                    point = endKey.substring(prefixLen, prefixLen + 1);
                    // 如果此时下一个字符比预定义的最后一个前缀大的话(如中文文件名的情况)说明后续根据预定义前缀再检索无意义,则直接返回即可
                    if (point.compareTo(lastPoint) > 0) {
                        point = null;
                    // 如果 point 比第一个预定义前缀小则设置 lister 的结束位置到第一个预定义前缀
                    } else if (point.compareTo(firstPoint) < 0) {
                        point = firstPoint;
                        lister.setEndPrefix(startPrefix + firstPoint);
                        lister.setLimit(2);
                    } else {
                        prefixesMap.put(startPrefix + point, new HashMap(){{ put("marker", lister.getMarker()); }});
                        lister.setEndPrefix(endKey);
                    }
                } else {
                    point = firstPoint;
                    // 无 next 时直接将 lister 的结束位置设置到第一个预定义前
                    lister.setEndPrefix(startPrefix + firstPoint);
                    lister.setLimit(2);
                }
            } else {
                return nextPoint(lister, true);
            }
        }
        return point;
    }

    private List> filteredListerByPrefixes(Stream prefixesStream) {
        List> prefixesLister = prefixesStream.map(prefix -> {
            try {
                return generateLister(prefix);
            } catch (SuitsException e) {
                try { FileUtils.createIfNotExists(errorLogFile); } catch (IOException ignored) {}
                errorLogger.error("generate lister failed by {}\t{}", prefix, prefixesMap.get(prefix), e);
                return null;
            }
        }).filter(generated -> {
            if (generated == null) return false;
            else if (generated.currents().size() > 0 || generated.hasNext()) return true;
            else {
                progressMap.remove(generated.getPrefix());
                generated.close();
                return false;
            }
        }).collect(Collectors.toList());
        if (prefixesLister.size() > 0) {
            IStorageLister lastLister = prefixesLister.stream().max(Comparator.comparing(IStorageLister::getPrefix)).get();
            Map map = prefixesMap.get(lastLister.getPrefix());
            if (map == null) {
                prefixAndEndedMap.put(lastLister.getPrefix(), new HashMap<>());
            } else if (!map.containsKey("remove")) {
                prefixAndEndedMap.put(lastLister.getPrefix(), map);
            }
        }
        Iterator> it = prefixesLister.iterator();
        while (it.hasNext()) {
            IStorageLister nLister = it.next();
            if(!nLister.hasNext() || (nLister.getEndPrefix() != null && !"".equals(nLister.getEndPrefix()))) {
                executorPool.execute(() -> listing(nLister));
                it.remove();
            }
        }
        return prefixesLister;
    }

    private void processNodeLister(IStorageLister lister) {
        if (lister.currents().size() > 0 || lister.hasNext()) {
            executorPool.execute(() -> listing(lister));
        } else {
            progressMap.remove(lister.getPrefix());
            lister.close();
        }
    }

    void recordListerByPrefix(String prefix) {
        Map map = prefixesMap.get(prefix);
        String record = map == null ? "{}" : JsonUtils.toJsonObject(map).toString();
        recordLister(prefix, record);
    }

    private List> computeToNextLevel(List> listerList) {
        if (hasAntiPrefixes) {
            return listerList.parallelStream().map(lister -> {
                List nextPrefixes = null;
                String finalPoint = nextPoint(lister, true);
                if (finalPoint != null) {
                    nextPrefixes = originPrefixList.stream().filter(prefix -> prefix.compareTo(finalPoint) >= 0)
                            .map(prefix -> lister.getPrefix() + prefix).filter(this::checkPrefix)
                            .peek(this::recordListerByPrefix).collect(Collectors.toList());
                }
                processNodeLister(lister);
                if (nextPrefixes != null) return filteredListerByPrefixes(nextPrefixes.stream());
                else return null;
            }).filter(Objects::nonNull).reduce((list1, list2) -> { list1.addAll(list2); return list1; }).orElse(null);
        } else {
            return listerList.parallelStream().map(lister -> {
                List nextPrefixes = null;
                String finalPoint = nextPoint(lister, true);
                if (finalPoint != null) {
                    nextPrefixes = originPrefixList.stream().filter(prefix -> prefix.compareTo(finalPoint) >= 0)
                            .map(prefix -> lister.getPrefix() + prefix)
                            .peek(this::recordListerByPrefix).collect(Collectors.toList());
                }
                processNodeLister(lister);
                if (nextPrefixes != null) return filteredListerByPrefixes(nextPrefixes.stream());
                else return null;
            }).filter(Objects::nonNull).reduce((list1, list2) -> { list1.addAll(list2); return list1; }).orElse(null);
        }
    }

    private List checkListerInPool(List> listerList, int cValue, int initTiny) {
        List extremePrefixes = null;
        int count = 0;
        IStorageLister iLister;
        Iterator> iterator;
        String prefix;
        String nextMarker;
        String start;
        Map endMap;
        Map prefixMap;
        int tiny = initTiny;
        int accUnit = initTiny / 2;
        while (!executorPool.isTerminated()) {
            if (count >= 1200) {
                if (listerList == null) {
                    count = 0;
                    continue;
                }
                iterator = listerList.iterator();
                while (iterator.hasNext()) {
                    iLister = iterator.next();
                    if(!iLister.hasNext()) iterator.remove();
                }
                if (listerList.size() > 0 && listerList.size() <= tiny) {
                    tiny = initTiny;
                    rootLogger.info("unfinished: {}, cValue: {}, to re-split prefixes...", listerList.size(), cValue);
                    for (IStorageLister lister : listerList) {
                        // lister 的 prefix 为 final 对象,不能因为 truncate 的操作之后被修改
                        prefix = lister.getPrefix();
                        nextMarker = lister.truncate();
                        // 防止 truncate 过程中原来的线程中丢失了 prefixAndEndedMap 的操作,这里再判断一次
                        endMap = prefixAndEndedMap.get(prefix);
                        prefixMap = new HashMap<>();
                        if (endMap == null) {
                            prefixMap.put("remove", "remove");
                        } else {
                            start = lister.currentEndKey();
                            if (start != null) endMap.put("start", start);
                        }
                        rootLogger.info("prefix: {}, nextMarker: {}, endMap: {}", prefix, nextMarker, endMap);
                        // 如果 truncate 时的 nextMarker 已经为空说明已经列举完成了
                        if (nextMarker == null || nextMarker.isEmpty()) continue;
                        if (extremePrefixes == null) extremePrefixes = new ArrayList<>();
                        extremePrefixes.add(prefix);
                        prefixMap.put("marker", nextMarker);
                        prefixesMap.put(prefix, prefixMap);
                    }
                } else if (listerList.size() <= cValue) {
                    tiny += accUnit;
                    count = 900;
                } else {
                    count = 0;
                }
                refreshRecordAndStatistics();
            }
            sleep(1000);
            count++;
        }
        return extremePrefixes;
    }

    private List lastEndedPrefixes() {
        List phraseLastPrefixes = new ArrayList<>(prefixAndEndedMap.keySet());
        phraseLastPrefixes.sort(Comparator.reverseOrder());
        String previousPrefix;
        Map prefixMap;
        String start;
        Set startPrefixes = prefixes == null ? new HashSet<>() : new HashSet<>(prefixes);
        for (String prefix : phraseLastPrefixes) {
            prefixMap = prefixAndEndedMap.get(prefix);
            rootLogger.info("prefix: {}, endMap: {}", prefix, prefixMap);
            if (prefixMap == null || prefixMap.size() == 0) {
                prefixAndEndedMap.remove(prefix);
                continue;
            }
//            recorder.remove(prefix);
            start = prefixMap.get("start");
            // 由于 marker 优先原则,为了 start 生效则将可能的 marker 删除
            if (start != null && !"".equals(start)) {
                prefixMap.remove("marker");
            } else {
                prefixAndEndedMap.remove(prefix);
                continue;
            }
            if (startPrefixes.contains(prefix)) {
                if (prefixRight) prefixAndEndedMap.put("", prefixMap);
                prefixAndEndedMap.remove(prefix);
            } else {
                previousPrefix = prefix.substring(0, prefix.length() - 1);
                prefixAndEndedMap.put(previousPrefix, prefixMap);
                prefixAndEndedMap.remove(prefix);
            }
        }
        prefixesMap.putAll(prefixAndEndedMap);
        phraseLastPrefixes = prefixAndEndedMap.keySet().stream().sorted()
                .peek(this::recordListerByPrefix).collect(Collectors.toList());
        return phraseLastPrefixes;
    }

    private void prefixesListing() {
        List> listerList = filteredListerByPrefixes(prefixes.parallelStream());
        while (listerList != null && listerList.size() > 0 && listerList.size() < threads) {
            prefixesMap.clear();
            listerList = computeToNextLevel(listerList);
        }
        if (listerList != null && listerList.size() > 0) {
            listerList.parallelStream().forEach(lister -> executorPool.execute(() -> listing(lister)));
        }
        executorPool.shutdown();
        if (threads > 1) {
            int cValue = threads >= 10 ? threads / 2 : 3;
            int tiny = threads >= 30 ? threads / 10 : threads >= 10 ? 3 : 1;
            List extremePrefixes = checkListerInPool(listerList, cValue, tiny);
            while (extremePrefixes != null && extremePrefixes.size() > 0) {
                extremePrefixes.parallelStream().forEach(this::recordListerByPrefix);
                executorPool = Executors.newFixedThreadPool(threads);
                listerList = filteredListerByPrefixes(extremePrefixes.parallelStream());
                while (listerList != null && listerList.size() > 0 && listerList.size() <= threads) {
                    prefixesMap.clear();
                    listerList = computeToNextLevel(listerList);
                }
                if (listerList != null && listerList.size() > 0) {
                    listerList.parallelStream().forEach(lister -> executorPool.execute(() -> listing(lister)));
                }
                executorPool.shutdown();
                extremePrefixes = checkListerInPool(listerList, cValue, tiny);
            }
        } else {
            while (!executorPool.isTerminated()) {
                sleep(2000);
                if (countInterval-- <= 0) {
                    countInterval = 300;
                    refreshRecordAndStatistics();
                }
            }
        }
        List phraseLastPrefixes = lastEndedPrefixes();
        if (phraseLastPrefixes.size() > 0) {
            executorPool = Executors.newFixedThreadPool(phraseLastPrefixes.size());
            listerList = filteredListerByPrefixes(phraseLastPrefixes.parallelStream());
            listerList.parallelStream().forEach(lister -> executorPool.execute(() -> listing(lister)));
            executorPool.shutdown();
        }
        while (!executorPool.isTerminated()) {
            sleep(2000);
            if (countInterval-- <= 0) {
                countInterval = 300;
                refreshRecordAndStatistics();
            }
        }
    }

    /**
     * 根据当前参数值创建多线程执行数据源导出工作
     */
    @Override
    public void export() throws Exception {
        String info = processor == null ? String.join(" ", "list objects from", getSourceName(), "bucket:", bucket) :
                String.join(" ", "list objects from", getSourceName(), "bucket:", bucket, "and", processor.getProcessName());
        rootLogger.info("{} running...", info);
        rootLogger.info("order\tprefix\tquantity");
        showdownHook();
        IStorageLister startLister = null;
        // 在初始化时即做检查,hasAntiPrefixes 的情况下 prefixes 不可能为空,所以在 prefixes 为空时,hasAntiPrefixes 一定为 false
        if (prefixes == null || prefixes.size() == 0) {
            recordListerByPrefix("");
            startLister = generateLister("", 2);
            startLister.setLimit(unitLen);
            if (threads > 1) {
                String finalPoint = nextPoint(startLister, false);
                if (finalPoint != null) {
                    IStorageLister lister = startLister;
                    prefixes = originPrefixList.parallelStream().filter(prefix -> prefix.compareTo(finalPoint) >= 0)
                            .map(prefix -> lister.getPrefix() + prefix).collect(Collectors.toList());
                }
            }
        } else {
            if (prefixLeft || "".equals(prefixes.get(0))) {
                recordListerByPrefix("");
                if ("".equals(prefixes.get(0))) prefixes.remove(0);
                prefixesMap.put("", new HashMap(){{ put("end", prefixes.get(0)); }});
                startLister = generateLister("", 2);
                startLister.setLimit(unitLen);
                prefixesMap.remove("");
            }
        }
        try {
            if (prefixes == null || prefixes.size() == 0) {
                if (hasAntiPrefixes) rootLogger.info("there are no prefixes to check anti-prefixes.");
                if (startLister.currents().size() > 0 || startLister.hasNext()) {
                    listing(startLister);
                } else {
                    progressMap.remove(startLister.getPrefix());
                    startLister.close();
                }
            } else {
                if (hasAntiPrefixes) {
                    prefixes = prefixes.parallelStream().filter(this::checkPrefix)
                            .peek(this::recordListerByPrefix).collect(Collectors.toList());
                } else {
                    prefixes.parallelStream().forEach(this::recordListerByPrefix);
                }
                executorPool = Executors.newFixedThreadPool(threads);
                if (startLister != null) processNodeLister(startLister);
                prefixesListing();
            }
            rootLogger.info("{} finished, results in {}.", info, savePath);
            endAction();
        } catch (Throwable e) {
//            executorPool.shutdownNow(); // 执行中的 sleep(), wait() 操作会抛出 InterruptedException
            stopped = true; // 使用该语句使线程池中的任务退出循环可能比直接 shutdownNow() 要好
            rootLogger.error("export failed", e);
            endAction();
            System.exit(-1);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy