Неизменяемость и перезагружаемые конфигурации

Обратите внимание: хотя в этом вопросе упоминается Java, я думаю, что это проблема ООП / параллелизма, и, вероятно, на нее может ответить любой, у кого есть значительный опыт программирования.


Итак, я создаю ConfigurationLoader, который будет читать Configuration POJO из удаленной службы и делать его доступным через API. Несколько вещей:

  • Как только ConfigurationLoader запрашивается Configuration в первый раз, фоновый поток (рабочий) будет проверять связь с удаленной службой каждые, скажем, 30 секунд, для получения обновлений, а затем применяет эти обновления к экземпляру Configuration; и
  • Если Configuration изменен, фоновый исполнитель будет уведомлен об изменении и отправит «новый» Configuration в удаленную службу;
  • И ConfigurationLoader, и Configuration должны быть поточно-ориентированными.

Поэтому, когда я думаю о «потокобезопасности», первое, о чем я думаю, - это неизменяемость, которая приводит меня к отличным проектам, таким как Неизменяемые. Проблема в том, что Configuration не может быть неизменным, потому что нам нужно иметь возможность изменить его на стороне клиента, а затем позволить загрузчику передать эти изменения обратно на сервер.

Следующей моей мыслью было попробовать сделать как ConfigurationLoader, так и Configuration синглтоны, но проблема в том, что ConfigurationLoader требует много аргументов для его создания, и поскольку этот отличный ответ указывает, что синглтон, который принимает аргументы при построении, не является истинным синглтоном.

// 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'
        }
    }
}

Какие здесь у меня варианты? Как мне создать и загрузчик, и конфигурацию, чтобы обеспечить потокобезопасность, когда конфигурация может изменяться на стороне клиента (по запросу) или асинхронно с помощью фонового рабочего нить?


person smeeb    schedule 15.09.2015    source источник


Ответы (2)


Проблема в том, что конфигурация не может быть неизменной, потому что мы должны иметь возможность ее изменить.

Он все еще может быть неизменным, вы просто создаете новый для каждого изменения («копирование при записи»).

Какие у меня здесь варианты?

Первое, о чем вам нужно подумать: как вы хотите реагировать на изменения конфигурации в одновременно выполняемых задачах? По сути, у вас есть три варианта:

Игнорировать изменение конфигурации, пока задача не будет выполнена

Т.е. какой-то каталог, в который ваши коды записывают файлы - завершите запись текущего файла в текущий целевой каталог, поместите новые файлы в новый каталог. Запись нескольких байтов в /new/path/somefile не будет хорошей идеей, если вы никогда не создавали этот файл. Лучшим вариантом для этого, вероятно, является неизменяемый объект Configuration, который вы храните в поле экземпляра задачи (т.е. при создании задачи - в этом случае вы также можете сделать это поле final для ясности). Обычно это работает лучше всего, если ваш код представляет собой набор изолированных небольших задач.

Плюсы: Конфигурация никогда не изменяется в рамках одной задачи, поэтому ее легко сделать безопасным и легко протестировать.

Минусы: обновления конфигурации никогда не попадают в уже запущенные задачи.

Проверяйте задачи на предмет изменений конфигурации

Т.е. ваша задача регулярно отправляет некоторые данные на адрес электронной почты. Создайте центральное хранилище для вашей конфигурации (например, в вашем псевдокоде) и повторно загрузите его через некоторый интервал (т.е. между сбором данных и отправкой почты) из кода вашей задачи. Обычно это лучше всего подходит для длительных / постоянных задач.

Плюсы: конфигурация может измениться во время выполнения задачи, но все же довольно просто, чтобы обезопасить себя - просто убедитесь, что у вас есть некоторый барьер памяти для чтения конфигурации (сделайте свое личное configuration поле volatile, используйте AtomicReference , охраняйте его замком, что угодно).

Минусы: код задачи будет сложнее протестировать, чем первый вариант. Значения конфигурации могут все еще быть устаревшими между проверками.

Конфигурация сигнала изменяет ваши задачи

В основном вариант два, но наоборот. Всякий раз, когда конфигурация изменяется, прерывайте свои задачи, обрабатывайте прерывание как сообщение «конфигурация требует обновления», продолжайте / перезапускайте с новой конфигурацией.

Плюсы: значения конфигурации никогда не устаревают.

Минусы: это труднее всего понять. Некоторые могут даже возразить, что вы не можете сделать это правильно, потому что прерывание следует использовать только для выполнения задачи прерывания беременности. Если вы разместите проверки обновлений своей задачи в правильных местах, то преимущества перед вторым вариантом будут очень незначительны (если они вообще есть). Не делайте этого, если у вас нет на то веской причины.

person duckstep    schedule 15.09.2015

Для этого вам нужен синглтон, но ваш синглтон не является неизменным. Это потокобезопасная вещь. Сделайте свой синглтон (конфигурацию) содержащим простой объект Properties или что-то в этом роде и защитите доступ к нему с помощью синхронизации. Ваш загрузчик конфигурации каким-то образом знает об этом синглтоне конфигурации и функциях для установки при синхронизации новых экземпляров объекта Properties при обнаружении изменений.

Я почти уверен, что конфигурация Apache Commons делает что-то подобное.

person Bob Kuhar    schedule 15.09.2015