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

com.qiniu.storage.UpHostHelper Maven / Gradle / Ivy

There is a newer version: 7.17.0
Show newest version
package com.qiniu.storage;

import com.qiniu.common.QiniuException;

import java.util.*;

class UpHostHelper {
    private long failedPeriodMillis; // 毫秒 1/1000 s
    private Configuration conf;

    private LinkedHashMap regionHostsLRU =
            new LinkedHashMap(1, 1.0f, true) {
                @Override
                protected boolean removeEldestEntry(Map.Entry eldest) {
                    return this.size() > 6;
                }
            };


    UpHostHelper(Configuration conf, int failedPeriodSeconds) {
        this.conf = conf;
        this.failedPeriodMillis = failedPeriodSeconds * 1000L;
    }


    String upHost(Region region, String upToken, String lastUsedHost, boolean changeHost, boolean mustReturnUpHost) throws QiniuException {
        RegionReqInfo regionReqInfo = new RegionReqInfo(upToken);

        // auto region may failed here.
        List accHosts;
        List srcHosts;
        try {
            accHosts = region.getAccUpHost(regionReqInfo);
            srcHosts = region.getSrcUpHost(regionReqInfo);
        } catch (QiniuException e) { // it will success soon.
            if (mustReturnUpHost && conf.useDefaultUpHostIfNone) {
                return failedUpHost("failed_get_region");
            } else {
                throw e;
            }
        }

        String regionKey = region.getRegion(regionReqInfo) + "_&//=_" + getRegionKey(srcHosts, accHosts);
        RegionUpHost regionHost = regionHostsLRU.get(regionKey);
        if (regionHost == null) {
            regionHost = new RegionUpHost();
            regionHostsLRU.put(regionKey, regionHost);
        }

        return regionHost.upHost(accHosts, srcHosts, lastUsedHost, changeHost);
    }

    private String failedUpHost(String regionKey) {
        List srcHosts;
        List accHosts;

        RegionUpHost regionHost = regionHostsLRU.get(regionKey);
        if (regionHost == null) {
            regionHost = new RegionUpHost();
            regionHostsLRU.put(regionKey, regionHost); // small ,do not need remove it.
        }
        if (regionHost.lastSrcHosts == null) {
            accHosts = new ArrayList() {
                {
                    this.add("upload.qiniup.com");
                    this.add("upload-z1.qiniup.com");
                    this.add("upload-z2.qiniup.com");
                    this.add("upload-na0.qiniup.com");
                    this.add("upload-as0.qiniup.com");
                }
            };
            Collections.shuffle(accHosts); // ?
            srcHosts = new ArrayList<>();
        } else {
            srcHosts = regionHost.lastSrcHosts;
            accHosts = regionHost.lastAccHosts;
        }
        String host = regionHost.upHost(accHosts, srcHosts, null, false);
        return host;
    }


    String getRegionKey(List srcHosts, List accHosts) {
        String host = null;
        String lhost = null;
        for (String a : srcHosts) {
            if (host == null) {
                host = a;
                lhost = a;
            }
            if (a.length() < host.length()) {
                host = a;
            }
            if (a.length() > lhost.length()) {
                lhost = a;
            }
        }
        if (host != null) {
            return host + ";+=-_" + lhost;
        }

        for (String a : accHosts) {
            if (host == null) {
                host = a;
                lhost = a;
            }
            if (a.length() < host.length()) {
                host = a;
            }
            if (a.length() > lhost.length()) {
                lhost = a;
            }
        }
        return host + ";+=-_" + lhost;
    }


    class RegionUpHost {
        List lastAccHosts;
        List lastSrcHosts;

        HashMap hostMark = new HashMap<>();
        ArrayList lastHosts;

        volatile int mainHostCount = 0;
        volatile String lastHost;

        private void initHostMark(List f, List s) {
            ArrayList _lastHosts = new ArrayList<>();
            int _mainHostCount = 0;

            if (f.size() > 0) {
                _lastHosts.add(f.get(0));
                _mainHostCount++;
            }
            if (s.size() > 0) {
                _lastHosts.add(s.get(0));
                _mainHostCount++;
            }
            // 后面的 host 乱序插入
            int l = Math.min(f.size(), s.size());
            int i = 1;

            Random r = new Random();
            ArrayList iidx = new ArrayList<>();

            for (; i < l; i++) {
                iidx.add(i);
            }
            randomAdd(_lastHosts, iidx, r, f, s);

            // f or s ,checked by i < f.size() and i < s.size()
            iidx.clear();
            for (; i < f.size(); i++) {
                iidx.add(i);
            }
            randomAdd(_lastHosts, iidx, r, f, null);

            iidx.clear();
            for (; i < s.size(); i++) {
                iidx.add(i);
            }
            randomAdd(_lastHosts, iidx, r, s, null);

            lastHosts = _lastHosts;
            mainHostCount = _mainHostCount;
        }

        private void randomAdd(ArrayList _lastHosts, ArrayList iidx, Random r,
                               List list1, List list2) {
            int size = iidx.size();
            for (int j = 0; j < size; j++) {
                int ji = r.nextInt(iidx.size());
                Integer jv = iidx.remove(ji);
                if (list1 != null) {
                    _lastHosts.add(list1.get(jv));
                }
                if (list2 != null) {
                    _lastHosts.add(list2.get(jv));
                }
            }
        }

        String upHost(List accHosts, List srcHosts, String lastUsedHost, boolean changeHost) {
            if (lastAccHosts != accHosts || lastSrcHosts != srcHosts) {
                lastAccHosts = accHosts;
                lastSrcHosts = srcHosts;
                synchronized (hostMark) {
                    hostMark.clear();
                    if (conf.accUpHostFirst) {
                        initHostMark(lastAccHosts, lastSrcHosts);
                    } else {
                        initHostMark(lastSrcHosts, lastAccHosts);
                    }
                    lastHost = lastHosts.get(0); // at last one domain
                }
            }

            long t = System.currentTimeMillis() - failedPeriodMillis;

            // acc src 的第一个,比如(upload.qiniup.com, up.qiniup.com), //
            // 作为主上传域名,可以继续使用,不用恢复到 lastHosts.get(0) //
            // 优先处理 //
            if (!changeHost) {
                Long el0 = hostMark.get(lastHost);
                if (el0 == null) {
                    el0 = 0L;
                }
                if (el0 < t) {
                    int lastUsedIdx = lastHosts.indexOf(lastHost);
                    if (lastUsedIdx > -1 && lastUsedIdx < mainHostCount) {
                        return lastHost;
                    }
                }
                if (lastUsedHost != null && !lastHost.equals(lastUsedHost)) {
                    Long el = hostMark.get(lastUsedHost);
                    if (el == null) {
                        el = 0L;
                    }
                    if (el < t) {
                        int lastUsedIdx = lastHosts.indexOf(lastUsedHost);
                        if (lastUsedIdx > -1 && lastUsedIdx < mainHostCount) {
                            lastHost = lastUsedHost;
                            return lastUsedHost;
                        }
                    }
                }
            }

            if (changeHost) {
                synchronized (hostMark) {
                    // uploader 使用的的 host 与 本地记录的 host 不一样,直接返回本地记录的 host,这也是新的 host //
                    // 减少 多个同时失败,大量同时更改 host,导致 将 host 该回到 老的无效 host //
                    String host = lastUsedHost != null ? lastUsedHost : lastHost;
                    if (!lastHost.equals(host)) {
                        Long el = hostMark.get(lastHost);
                        if (el == null) {
                            el = 0L;
                        }
                        if (el < t) {
                            return lastHost;
                        }
                    }
                    // 标记当前 host 不可用 //
                    hostMark.put(host, System.currentTimeMillis());
                }
            }


            // 下方 未加锁,仍有可能 重复更改 host ,加 return lastHost; 可能会减少这种几率 //
            String _lastHost = lastHost;

            // 标记过期后,恢复到 优先使用 第一个 值 //
            for (String h : lastHosts) {
                Long e = hostMark.get(h);
                if (e == null) {
                    e = 0L;
                }
                // 不可用标志已过期,即域名可以使用 //
                if (e < t) {
                    lastHost = h;
                    return h;
                } else {
                    // 单线程,下面代码没什么意义. //
                    // 其它线程调用了 upHost(true),可能改变 lastHost 状态,提前返回,不用进入 "全部标记为不可用" //
                    if (_lastHost != lastHost) {
                        return lastHost;
                    }
                }
            }

            // 全部标记为不可用 //
            // 清空所有标记,重新开始 //
            synchronized (hostMark) {
                hostMark.clear();
                lastHost = lastHosts.get(0); // must be in synchronized .  lastHosts.clear() //
            }
            return lastHost;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy