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

标签 java multithreading static couchbase static-block

我将 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;

我不明白为什么调用在它不是静态初始化 block 的一部分时会成功。静态初始化 block 是否存在任何我不知道的与性能相关的已知漏洞?

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

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 字段。

关于java - Couchbase:从静态代码块中初始化需要更长的时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33982030/

相关文章:

java - Kafka Stream 的交互式调节

c - 防止多个线程只访问数组的一个元素(而不是整个数组)

php - PHP中的公共(public)静态函数和静态公共(public)函数有什么区别?

用于定义公共(public)变量 static const integer 的 C++ 标准

c# - 为每个用户创建不同的静态单例实例

java - 如何在 Java Rest API 中处理扩展 ascii 字符

java - 在java中使用XOauth2通过IMAP连接到microsoft Outlook.com

java - 滚动后自定义 ListView 崩溃

java - 游戏循环帧独立

java - Java中的多线程文件读取