java – Couchbase:从静态代码块中初始化需要更长时间

我把我的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;
}

最佳答案 第一:你正在做双重检查的成语.这总是很糟糕.

只放一个if(BLACKLIST == null)并且它必须在synchronized中.

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

点赞