Перейти к основному содержимому

Руководство по флипам на весну

· 6 мин. чтения

1. Обзор

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

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

2. Зависимость от Maven

Прежде чем мы начнем, нам нужно добавить библиотеку Flips в наш pom.xml:

<dependency>
<groupId>com.github.feature-flip</groupId>
<artifactId>flips-core</artifactId>
<version>1.0.1</version>
</dependency>

У Maven Central есть последняя версия библиотеки , а проект Github здесь .

Конечно, нам также нужно включить Spring:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.5.10.RELEASE</version>
</dependency>

Поскольку Flips еще не совместим с Spring версии 5.x, мы собираемся использовать последнюю версию Spring Boot в ветке 4.x.

3. Простой REST-сервис для флипов

Давайте составим простой проект Spring Boot для добавления и переключения новых функций и флагов.

Наше REST-приложение предоставит доступ к ресурсам Foo :

public class Foo {
private String name;
private int id;
}

Мы просто создадим Service , который поддерживает список Foos :

@Service
public class FlipService {

private List<Foo> foos;

public List<Foo> getAllFoos() {
return foos;
}

public Foo getNewFoo() {
return new Foo("New Foo!", 99);
}
}

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

И, конечно же, нам нужно создать контроллер:

@RestController
public class FlipController {

private FlipService flipService;

// constructors

@GetMapping("/foos")
public List<Foo> getAllFoos() {
return flipService.getAllFoos();
}
}

4. Функции управления на основе конфигурации

Самое простое использование Flips — это включение или отключение функции на основе конфигурации. У Flips есть несколько аннотаций для этого.

4.1. Свойство окружающей среды

Давайте представим, что мы добавили новую возможность в FlipService ; получение Foos по их идентификатору.

Добавим новый запрос в контроллер:

@GetMapping("/foos/{id}")
@FlipOnEnvironmentProperty(
property = "feature.foo.by.id",
expectedValue = "Y")
public Foo getFooById(@PathVariable int id) {
return flipService.getFooById(id)
.orElse(new Foo("Not Found", -1));
}

@FlipOnEnvironmentProperty определяет, доступен ли этот API.

Проще говоря, когда feature.foo.by.id равен Y , мы можем делать запросы по Id. Если это не так (или вообще не определено), Flips отключит метод API.

Если функция не включена, Flips выдаст FeatureNotEnabledException, а Spring вернет клиенту REST сообщение «Не реализовано».

Когда мы вызываем API со свойством, установленным на N , мы видим следующее:

Status = 501
Headers = {Content-Type=[application/json;charset=UTF-8]}
Content type = application/json;charset=UTF-8
Body = {
"errorMessage": "Feature not enabled, identified by method
public com.foreach.flips.model.Foo
com.foreach.flips.controller.FlipController.getFooById(int)",
"className":"com.foreach.flips.controller.FlipController",
"featureName":"getFooById"
}

Как и ожидалось, Spring перехватывает FeatureNotEnabledException и возвращает клиенту статус 501.

4.2. Активный профиль

Spring уже давно дает нам возможность сопоставлять bean-компоненты с различными профилями , такими как dev , test или prod . Расширение этой возможности для сопоставления флагов функций с активным профилем имеет интуитивно понятный смысл.

Давайте посмотрим, как функции включаются или отключаются на основе активного профиля Spring :

@RequestMapping(value = "/foos", method = RequestMethod.GET)
@FlipOnProfiles(activeProfiles = "dev")
public List getAllFoos() {
return flipService.getAllFoos();
}

Аннотация @FlipOnProfiles принимает список имен профилей. Если активный профиль есть в списке, API доступен.

4.3. Весенние выражения

Язык выражений Spring (SpEL) — это мощный механизм для управления средой выполнения. У Flips есть способ переключать функции.

@FlipOnSpringExpression переключает метод, основанный на выражении SpEL, который возвращает логическое значение.

Давайте используем простое выражение для управления новой функцией:

@FlipOnSpringExpression(expression = "(2 + 2) == 4")
@GetMapping("/foo/new")
public Foo getNewFoo() {
return flipService.getNewFoo();
}

4.4. Запрещать

Чтобы полностью отключить функцию, используйте @FlipOff :

@GetMapping("/foo/first")
@FlipOff
public Foo getFirstFoo() {
return flipService.getLastFoo();
}

В этом примере getFirstFoo() полностью недоступен.

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

5. Функции управления с датой/временем

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

5.1. Дата и время

@FlipOnDateTime принимает имя свойства, отформатированное в формате ISO 8601 .

Итак, давайте установим свойство, указывающее новую функцию, которая будет активна 1 марта:

first.active.after=2018-03-01T00:00:00Z

Затем мы напишем API для получения первого Foo:

@GetMapping("/foo/first")
@FlipOnDateTime(cutoffDateTimeProperty = "first.active.after")
public Foo getFirstFoo() {
return flipService.getLastFoo();
}

Flips проверит указанное свойство. Если свойство существует и указанная дата/время прошли, функция включена.

5.2. День недели

Библиотека предоставляет @FlipOnDaysOfWeek , что полезно для таких операций, как A/B-тестирование:

@GetMapping("/foo/{id}")
@FlipOnDaysOfWeek(daysOfWeek={DayOfWeek.MONDAY, DayOfWeek.WEDNESDAY})
public Foo getFooByNewId(@PathVariable int id) {
return flipService.getFooById(id).orElse(new Foo("Not Found", -1));
}

getFooByNewId() доступен только по понедельникам и средам.

6. Замените бин

Включение и выключение методов полезно, но мы можем захотеть ввести новое поведение через новые объекты. @FlipBean указывает Flips вызывать метод в новом bean-компоненте.

Аннотация Flips может работать с любым компонентом Spring @Component. Пока что мы изменили только наш @RestController , давайте попробуем изменить наш Service.

Мы создадим новый сервис с поведением, отличным от FlipService :

@Service
public class NewFlipService {
public Foo getNewFoo() {
return new Foo("Shiny New Foo!", 100);
}
}

Мы заменим getNewFoo() старой службы новой версией:

@FlipBean(with = NewFlipService.class)
public Foo getNewFoo() {
return new Foo("New Foo!", 99);
}

Flips будет направлять вызовы getNewThing() в NewFlipService. @FlipBean — еще один переключатель, наиболее полезный в сочетании с другими. Давайте посмотрим на это сейчас.

7. Объединение переключателей

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

Давайте объединим два наших предыдущих примера:

@FlipBean(
with = NewFlipService.class)
@FlipOnEnvironmentProperty(
property = "feature.foo.by.id",
expectedValue = "Y")
public Foo getNewFoo() {
return new Foo("New Foo!", 99);
}

Мы воспользовались новой настраиваемой услугой.

8. Заключение

В этом кратком руководстве мы создали простую службу Spring Boot и включали и выключали API с помощью аннотаций Flips. Мы увидели, как функции переключаются с помощью информации о конфигурации и даты/времени, а также как функции можно переключать путем замены bean-компонентов во время выполнения.

Образцы кода, как всегда, можно найти на GitHub .