不变性和可重载配置

Immutability and Reloadable Configs

请注意: 虽然这个问题提到了 Java,但我认为它本质上是一个 OOP/concurrency 问题,任何具有重要编程经验的人都可以回答经验.


所以我正在构建一个 ConfigurationLoader,它将从远程服务读取 Configuration POJO,并通过 API 使其可用。一些事情:

所以当我想到"thread safety"时,我首先想到的是不变性,这让我走向了像Immutables这样的优秀项目。问题是 Configuration 不能是不可变的,因为我们需要能够在客户端更改它,然后让加载程序将这些更改传回服务器。

我的下一个想法是尝试使 ConfigurationLoaderConfiguration 都成为单例,但问题是 ConfigurationLoader 需要很多参数来实例化它,并且作为this excellent answer points out,在构造中接受参数的单例不是真正的单例。

// Groovy pseudo-code
class Configuration {
    // Immutable? Singleton? Other?
}

class ConfigurationLoader {
    // private fields
    Configuration configuration

    ConfigurationLoader(int fizz, boolean buzz, Foo foo, List<Bar> bars) {
        super()

        this.fizz = fizz
        this.buzz = buzz;
        // etc.
    }

    Configuration loadConfiguration() {
        if(configuration == null) {
            // Create background worker that will constantly update
            // 'configuration'
        }
    }
}

我在这里有什么选择?我如何创建线程安全的加载程序和配置,其中配置可由客户端更改(在-需求)或由后台工作线程异步?

你需要一个单身人士来完成这个,但你的单身人士并不是一成不变的。它是线程安全的东西。让你的单身人士(配置)包含一个简单的 Properties 对象或其他东西,并通过同步保护对此的访问。您的 Configuration Loader 以某种方式知道此 Configuration 单例和功能,以便在检测到更改时在同步下设置 Properties 对象的新实例。

我很确定 Apache Commons 配置会做这样的事情。

The problem is that Configuration can't be immutable because we need to be able to change it

它仍然可以是不可变的,你只需为每次更改创建一个新的("copy-on-write")。

What are my options here?

您首先要考虑的是:您希望如何对并发 运行ning 任务中的配置更改做出反应?基本上,您有三个选择:

在任务完成之前忽略配置更改

即您的代码将文件写入的某个目录 - 完成将当前文件写入当前目标目录,将新文件放入新目录。如果您从未创建该文件,则将一些字节写入 /new/path/somefile 将不是一个好主意。最好的选择可能是存储在任务实例字段中的不可变 Configuration 对象(即在任务创建时 - 在这种情况下,为了清楚起见,您也可以将该字段设置为 final )。如果您的代码被设计为孤立的小任务的集合,这通常效果最好。

优点:配置在单个任务中永远不会改变,因此这很容易获得安全性和易于测试。

缺点:配置更新从未完成 运行ning 任务。

让您的任务检查配置更改

即您的任务会定期将一些数据发送到电子邮件地址。为您的配置(如在您的伪代码中)设置一个中央存储,并在某个时间间隔(即在收集数据和发送邮件之间)从您的任务代码中重新获取它。这通常最适合 long-running/permanent 任务。

优点:配置 可以 在任务期间更改 运行,但要确保安全仍然有些简单 - 只要确保您有一些内存屏障来读取配置(让你的私人configuration字段volatile,使用AtomicReference,用锁保护它,无论如何)。

缺点:任务代码将比第一个选项更难测试。检查之间的配置值可能仍然过时。

信号配置更改为您的任务

基本上是选项二,但反过来。每当配置更改时,中断您的任务,将中断作为 "config needs updating" 消息处理,continue/restart 使用新配置。

优点:配置值永远不会过时。

缺点:这是最难做到的。有些人甚至会争辩说你不能做对,因为中断应该只用于任务中止。如果您将任务的更新检查放在正确的位置,则与第二个选项相比只有很小的好处(如果有的话)。如果您没有充分的理由,请不要这样做。