Флаги с пружинами

Особые флаги с пружиной

1. обзор

В этой статье мы кратко определим флаги функций и предложим самоуверенный и прагматичный подход к их реализации в приложениях Spring Boot. Затем мы рассмотрим более сложные итерации с использованием преимуществ различных функций Spring Boot.

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

2. Флаги

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

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

Как и во многих других ситуациях в программной инженерии, важно попытаться использовать наиболее простой подход, который решает текущую проблему, не добавляя ненужных сложностей.

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

Существует много сценариев, в которых флаги функций могут пригодиться:

Магистральная разработка и нетривиальные особенности

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

Конфигурация для конкретной среды

Нам может потребоваться определенная функциональность для сброса нашей БД для среды тестирования E2E.

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

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

A/B testing

Выпуск нескольких решений для одной и той же проблемы и измерение воздействия - это убедительный метод, который мы могли бы реализовать, используя флаги функций.

Канарейка

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

В следующих разделах мы постараемся предоставить практический подход к решению вышеупомянутых сценариев.

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

3. Флаги функций уровня приложения

Если нам нужно решить любой из первых двух вариантов использования, флаги функций уровня приложения - это простой способ заставить все работать.

Простой флаг объекта обычно включает свойство и некоторую конфигурацию, основанную на значении этого свойства.

3.1. Флаги функций с использованием пружинных профилей

Весной мы можемtake advantage of profiles. Conveniently, profiles enable us to configure certain beans selectively. With a few constructs around them, we can quickly create a simple and elegant solution for application-level feature flags.

Представим, что мы создаем систему майнинга BitCoin. Наше программное обеспечение уже запущено в производство, и нам поставили задачу создать экспериментальный улучшенный алгоритм майнинга.

В нашемJavaConfig мы можем профилировать наши компоненты:

@Configuration
public class ProfiledMiningConfig {

    @Bean
    @Profile("!experimental-miner")
    public BitcoinMiner defaultMiner() {
        return new DefaultBitcoinMiner();
    }

    @Bean
    @Profile("experimental-miner")
    public BitcoinMiner experimentalMiner() {
        return new ExperimentalBitcoinMiner();
    }
}

Затемwith the previous configuration, we simply need to include our profile to opt-in for our new functionality. Естьtons of ways of configuring our app в целом иenabling profiles in particular. Точно так же естьtesting utilities, чтобы облегчить нашу жизнь.

Пока наша система достаточно проста,we could then create an environment-based configuration to determine which features flags to apply and which ones to ignore.

Представим, что у нас есть новый интерфейс, основанный на карточках, а не на таблицах, вместе с предыдущим экспериментальным майнером.

Мы хотели бы включить обе функции в нашей среде принятия (UAT). Мы могли бы создать файлapplication-uat.yml:

spring:
  profiles:
    include: experimental-miner,ui-cards

# More config here

Имея предыдущий файл, нам просто нужно было включить профиль UAT в среде UAT, чтобы получить желаемый набор функций.

Также важно понимать, как использоватьspring.profiles.include. По сравнению сspring.profiles.active, первый позволяет нам добавлять профили аддитивным образом.

В нашем случае мы хотим, чтобы профильuat также включал экспериментальный майнер и пользовательские карты.

3.2. Флаги функций с использованием настраиваемых свойств

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

Для этих сценариев пользовательские свойства могут быть желательным вариантом.

Let’s rewrite our previous example taking advantage of @ConditionalOnProperty and our namespace:

@Configuration
public class CustomPropsMiningConfig {

    @Bean
    @ConditionalOnProperty(
      name = "features.miner.experimental",
      matchIfMissing = true)
    public BitcoinMiner defaultMiner() {
        return new DefaultBitcoinMiner();
    }

    @Bean
    @ConditionalOnProperty(
      name = "features.miner.experimental")
    public BitcoinMiner experimentalMiner() {
        return new ExperimentalBitcoinMiner();
    }
}

Предыдущий пример основан на условной конфигурации Spring Boot и настраивает тот или иной компонент в зависимости от того, установлено ли свойство наtrue илиfalse (или вообще опущено).

Результат очень похож на тот, что был в 3.1, но теперь у нас есть пространство имен. Наличие нашего пространства имен позволяет нам создавать значимые файлы YAML / properties:

#[...] Some Spring config

features:
  miner:
    experimental: true
  ui:
    cards: true

#[...] Other feature flags

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

Это может показаться небольшой деталью, но по мере роста и усложнения нашего приложения эта простая итерация поможет нам контролировать флаги наших функций.

Давайте поговорим о других преимуществах этого подхода.

3.3. Использование @ConfigurationProperties

Как только мы получим набор свойств с префиксом, мы можем создатьPOJO decorated with @ConfigurationProperties, чтобы получить программный дескриптор в нашем коде.

Следуя нашему постоянному примеру:

@Component
@ConfigurationProperties(prefix = "features")
public class ConfigProperties {

    private MinerProperties miner;
    private UIProperties ui;

    // standard getters and setters

    public static class MinerProperties {
        private boolean experimental;
        // standard getters and setters
    }

    public static class UIProperties {
        private boolean cards;
        // standard getters and setters
    }
}

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

3.4. Отображение конфигурации функции

Наша система майнинга биткойнов получила обновление пользовательского интерфейса, которое еще не полностью готово. По этой причине мы решили отметить его. У нас может быть одностраничное приложение, использующее React, Angular или Vue.

Независимо от технологии,we need to know what features are enabled so that we can render our page accordingly.

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

@RestController
public class FeaturesConfigController {

    private ConfigProperties properties;

    // constructor

    @GetMapping("/feature-flags")
    public ConfigProperties getProperties() {
        return properties;
    }
}

Могут быть более сложные способы обслуживания этой информации, напримерcreating custom actuator endpoints. Но ради этого руководства конечная точка контроллера кажется достаточно хорошим решением.

3.5. Содержание лагеря в чистоте

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

Feature flags for the first use case – trunk-based development and non-trivial features – are typically short-lived. Это означает, что нам нужно будет убедиться, что нашConfigProperties,, наша конфигурация Java, и наши файлыYAML остаются чистыми и актуальными.

4. Более подробные флаги функций

Иногда мы попадаем в более сложные сценарии. Для A / B-тестирования или канареечных релизов нашего предыдущего подхода просто недостаточно.

Чтобы получить подробные флаги функций, нам может потребоваться создать наше решение. Это может включать настройку нашей пользовательской сущности для включения информации о функциях или, возможно, расширение нашей веб-инфраструктуры.

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

В качестве альтернативы мы могли бы воспользоваться некоторыми встроенными инструментамиsuch as Togglz. Этот инструмент добавляет сложности, но предлагает отличное готовое решение иprovides first-class integration with Spring Boot.

Togglz поддерживает разныеactivation strategies:

  1. Username: Флаги, связанные с конкретными пользователями

  2. Gradual rollout: Флаги включены для определенного процента пользователей. Это полезно для выпусков Canary, например, когда мы хотим проверить поведение наших функций

  3. Release date: Мы можем запланировать включение флагов на определенную дату и время. Это может быть полезно для запуска продукта, согласованного выпуска или предложений и скидок.

  4. Client IP: Отмеченные функции на основе IP-адресов клиентов. Это может пригодиться при применении определенной конфигурации к конкретным клиентам, если они имеют статические IP-адреса.

  5. Server IP: В этом случае IP-адрес сервера используется для определения, должна ли функция быть включена или нет. Это может быть полезно и для канареечных релизов, с подходом, немного отличающимся от постепенного, например, когда мы хотим оценить влияние на производительность в наших случаях

  6. ScriptEngine: Мы можем включить флаги функций на основеarbitrary scripts. Это, пожалуй, самый гибкий вариант

  7. System Properties: Мы можем установить определенные системные свойства для определения состояния флага функции. Это было бы очень похоже на то, что мы достигли с нашим самым простым подходом

5. Резюме

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

Мы начали с определения того, как этот шаблон может помочь нам с несколькими распространенными случаями использования.

Затем мы создали несколько простых решений, используя готовые инструменты Spring и Spring Boot. С этим мы пришли к простой, но мощной конструкции пометки функций.

Внизу мы сравнили пару альтернатив. Переход от более простого и менее гибкого решения к более сложному, хотя и более сложному, шаблону.

Наконец, мы кратко представили несколько рекомендаций по созданию более надежных решений. Это полезно, когда нам нужна более высокая степень детализации.