4

我将我的 couchbase 初始化代码放在一个静态代码块中:

static {
        initCluster();
        bucket = initBucket("graph");
        metaBucket = initBucket("meta");
        BLACKLIST = new SetObservingCache<String>(() -> getBlackList(), BLACKLIST_REFRESH_INTERVAL_SEC * 1000); 
       }

我知道这不是一个好习惯,但它非常方便并且达到了它的目的,因为我需要这段代码在多线程环境中只运行一次,并阻止来自其他线程的所有后续调用,直到它完成(黑名单已被初始化)。

令我惊讶的是,对 getBlacklist() 的调用超时并且无法完成。但是,当 2 分钟后再次调用它时(就是这样ObservingCache做的),它在不到一秒的时间内完成。

为了解决这个问题,我重构了我的代码,使黑名单获取变得懒惰:

    public boolean isBlacklisted(String key) {
        // BLACKLIST variable should NEVER be touched outside of this context.
        assureBlacklistIsPopulated();
        return BLACKLIST != null ? BLACKLIST.getItems().contains(key) : false;
    }

    private void assureBlacklistIsPopulated() {
        if (!ENABLE_BLACKLIST) {
            return;
        }
        if (BLACKLIST == null) {
            synchronized (CouchConnectionManager.class) {
                if (BLACKLIST == null) {
                    BLACKLIST = new SetObservingCache<String>(() -> getBlackList(), BLACKLIST_REFRESH_INTERVAL_SEC * 1000);
                }
            }
        }
    }

调用isBlacklisted()阻塞所有其他尝试检查条目是否被列入黑名单的线程,直到黑名单被初始化。我不是这个解决方案的忠实拥护者,因为它非常冗长且容易出错——人们可能会尝试从 BLACKLIST 中读取而不assureBlacklistIsPopulated()事先调用。

类中的静态(非最终)字段如下:

private static CouchbaseCluster cluster;
private static Bucket bucket;
private static Bucket metaBucket;
private static SetObservingCache<String> BLACKLIST;

当它不是静态初始化块的一部分时,我无法弄清楚为什么调用会成功。是否存在我不知道的静态初始化块的任何已知与性能相关的漏洞?

编辑:每个请求添加初始化代码

private Bucket initBucket(String bucketName) {
    while(true) {
        Throwable t = null;
        try {
            ReportableThread.updateStatus("Initializing bucket " + bucketName);
            return cluster.openBucket(bucketName);
        } catch(Throwable t1) {
            t1.printStackTrace();
            t = t1;
        }
        try {
            ReportableThread.updateStatus(String.format("Failed to open bucket: %s reason: %s", bucketName,  t));
            Thread.sleep(500);              
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

private void initCluster() {
    CouchbaseEnvironment env = DefaultCouchbaseEnvironment
            .builder()
            .kvTimeout(MINUTE)
            .connectTimeout(MINUTE)
            .retryStrategy(FailFastRetryStrategy.INSTANCE)
            .requestBufferSize(16384 * 2)
            .responseBufferSize(16384 * 2)
            .build();
    while(true) {
        ReportableThread.updateStatus("Initializing couchbase cluster");
        Throwable t = null;
        try {
            cluster = CouchbaseCluster.create(env, getServerNodes());
            if(cluster != null) {
                return;
            }
        } catch(Throwable t1) {
            t1.printStackTrace();
            t = t1;
        }
        try {
            ReportableThread.updateStatus(String.format("Failed to create connection to couch %s", t));
            Thread.sleep(500);              
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public Set<String> getBlackList() {
    ReportableThread.updateStatus("Getting black list");
    AbstractDocument<?> abstractDoc = get("blacklist", metaBucket, JsonArrayDocument.class);
    JsonArrayDocument doc = null;
    if (abstractDoc != null && abstractDoc instanceof JsonArrayDocument) {
        doc = (JsonArrayDocument)abstractDoc;
    } else {
        return new HashSet<String>();
    }
    ReportableThread.updateStatus(String.format("%s: Got %d items | sorting items", new Date(System.currentTimeMillis()).toString(), doc.content().size()));
    HashSet<String> ret = new HashSet<String>();
    for (Object string : doc.content()) {
        if (string != null) {
            ret.add(string.toString());             
        }
    }
    return ret;
}
4

1 回答 1

1

第一:你正在做双重检查成语。那总是很糟糕。只放一个 if(BLACKLIST==null) 并且它必须在同步的内部。

第二:惰性初始化很好,但是在静态 getInstance() 中执行它并且永远不要公开 BLACKLIST 字段。

于 2016-02-07T14:30:45.717 回答