В этой статье познакомимся со Spring Cloud OpenFeign – декларативным REST-клиентом для приложений Spring Boot.
Feign упрощает написание клиентов веб-сервисов благодаря поддержке подключаемых аннотаций, включая аннотации Feign и JAX-RS.
Кроме того, Spring Cloud добавляет поддержку аннотаций Spring MVC и использования тех же HttpMessageConverters, что и в Spring Web.
Одна из замечательных особенностей использования Feign заключается в том, что не нужно писать никакого кода для вызова сервиса, кроме определения интерфейса.
Зависимости
Начнем с создания веб-проекта Spring Boot и добавления зависимости spring-cloud-starter-openfeign в файл pom.xml:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
Кроме того, необходимо добавить spring-cloud-dependencies:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Можно найти последние версии зависимостей spring-cloud-starter-openfeign и spring-cloud в центральном репозитории Maven.
Feign Client
Далее необходимо добавить @EnableFeignClients в основной класс:
@SpringBootApplication
@EnableFeignClients
public class ExampleApplication {
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
}
С помощью этой аннотации включаем сканирование компонентов для интерфейсов, которые заявляют, что они являются клиентами Feign. Затем объявляем клиента Feign с помощью аннотации @FeignClient:
@FeignClient(value = "jplaceholder", url = "https://jsonplaceholder.typicode.com/")
public interface JSONPlaceHolderClient {
@RequestMapping(method = RequestMethod.GET, value = "/posts")
List<Post> getPosts();
@RequestMapping(method = RequestMethod.GET, value = "/posts/{postId}", produces = "application/json")
Post getPostById(@PathVariable("postId") Long postId);
}
В этом примере настроили клиента для чтения из API-интерфейсов JSONPlaceholder.
Аргумент value, передаваемый в аннотации @FeignClient, является обязательным произвольным именем клиента, а аргумент url указывает базовый URL-адрес API. Кроме того, поскольку этот интерфейс является клиентом Feign, можно использовать аннотации Spring Web для объявления API, к которым хотим обратиться.
Конфигурация
Теперь очень важно понимать, что каждый клиент Feign состоит из набора настраиваемых компонентов.
Spring Cloud создает новый набор по умолчанию по запросу для каждого именованного клиента, используя класс FeignClientsConfiguration, который можно настроить, как описано в следующем разделе.
Приведенный выше класс содержит следующие bean-компоненты:
- Decoder – ResponseEntityDecoder, обертывающий SpringDecoder, используемый для декодирования Response;
- Encoder – SpringEncoder используется для кодирования RequestBody;
- Logger – Slf4jLogger является регистратором по умолчанию, который использует Feign;
- Contract – SpringMvcContract, обеспечивающий обработку аннотаций;
- Feign-Builder – HystrixFeign.Builder используется для создания компонентов;
- Client – LoadBalancerFeignClient или клиент Feign по умолчанию.
Пользовательская конфигурация бинов
Если хотим настроить один или несколько из этих bean-компонентов, то можно переопределить их с помощью класса @Configuration, который затем добавим в аннотацию FeignClient:
@FeignClient(value = "jplaceholder",
url = "https://jsonplaceholder.typicode.com/",
configuration = MyClientConfiguration.class)
@Configuration
public class MyClientConfiguration {
@Bean
public OkHttpClient client() {
return new OkHttpClient();
}
}
В этом примере указываем Feign использовать OkHttpClient вместо клиента по умолчанию для поддержки HTTP/2.
Feign поддерживает несколько клиентов для разных вариантов использования, включая ApacheHttpClient, который отправляет больше заголовков с запросом, например, Content-Length, что ожидают некоторые серверы.
Чтобы использовать эти клиенты, не забудем добавить необходимые зависимости в файл pom.xml:
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
Можно найти последние версии feign-okhttp и feign-httpclient в центральном репозитории Maven.
Конфигурация с использованием свойств
Вместо использования класса @Configuration можно использовать свойства приложения для настройки клиентов Feign, как показано в примере application.yaml ниже:
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
loggerLevel: basic
В этой конфигурации устанавливаем время ожидания в пять секунд и уровень ведения логирования в basic для каждого объявленного клиента в приложении.
Наконец, можно создать конфигурацию по умолчанию с именем клиента для настройки всех объектов @FeignClient или объявить имя клиента Feign для конфигурации:
feign:
client:
config:
jplaceholder:
Если есть как bean-компонент @Configuration, так и свойства конфигурации, то свойства конфигурации переопределяют значения @Configuration.
Перехватчики
Добавление перехватчиков – еще одна полезная функция, предоставляемая Feign.
Перехватчики могут выполнять множество неявных задач, от аутентификации до регистрации, для каждого HTTP-запроса/ответа.
В этом разделе реализуем собственный перехватчик, а также используем готовый вариант, предоставляемый Spring Cloud OpenFeign. Оба будут добавлять базовый заголовок аутентификации к каждому запросу.
Реализация RequestInterceptor
Реализуем собственный перехватчик запросов:
@Bean
public RequestInterceptor requestInterceptor() {
return requestTemplate -> {
requestTemplate.header("user", username);
requestTemplate.header("password", password);
requestTemplate.header("Accept", ContentType.APPLICATION_JSON.getMimeType());
};
}
Чтобы добавить перехватчик в цепочку запросов, необходимо добавить этот bean-компонент в класс @Configuration или объявить его в файле свойств:
feign:
client:
config:
default:
requestInterceptors:
com.baeldung.cloud.openfeign.JSONPlaceHolderInterceptor
Использование BasicAuthRequestInterceptor
В качестве альтернативы можно использовать класс BasicAuthRequestInterceptor, который предоставляет Spring Cloud OpenFeign:
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("username", "password");
}
Теперь все запросы будут содержать базовый заголовок аутентификации.
Поддержка Hystrix
Feign поддерживает Hystrix, поэтому, если его включили, то можно реализовать паттерн fallback (резерв).
При использовании паттерна fallback при сбое вызова удаленного сервиса вместо создания исключения потребитель сервиса выполнит альтернативный код, чтобы попытаться выполнить действие другим способом.
Для этого необходимо включить Hystrix, добавив feign.hystrix.enabled=true в файл свойств.
Это позволит реализовать методы fallback, которые вызываются при сбое сервиса:
@Component
public class JSONPlaceHolderFallback implements JSONPlaceHolderClient {
@Override
public List<Post> getPosts() {
return Collections.emptyList();
}
@Override
public Post getPostById(Long postId) {
return null;
}
}
Чтобы сообщить Feign, что методы fallback были предоставлены, необходимо настроить класс fallback в аннотации @FeignClient:
@FeignClient(value = "jplaceholder",
url = "https://jsonplaceholder.typicode.com/",
fallback = JSONPlaceHolderFallback.class)
public interface JSONPlaceHolderClient {
// APIs
}
7. Логирование
Для каждого клиента Feign по умолчанию создается logger.
Чтобы включить логирование, необходимо объявить его в файле application.properties, используя имя пакета клиентских интерфейсов:
logging.level.com.baeldung.cloud.openfeign.client: DEBUG
Или, если хотим включить логирование только для одного конкретного клиента в пакете, можно использовать полное имя класса:
logging.level.com.baeldung.cloud.openfeign.client.JSONPlaceHolderClient: DEBUG
Обратите внимание, что логирование Feign возможно только на уровне DEBUG.
Logger.Level, который можно настроить для каждого клиента, указывает уровень логирования:
@Configuration
public class ClientConfiguration {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.BASIC;
}
}
На выбор предлагается четыре уровня логирования:
- NONE – без логирования, значение по умолчанию;
- BASIC – отслеживать только метод запроса, URL-адрес и статус ответа;
- HEADERS – отслеживается основная информация вместе с заголовками запросов и ответов;
- FULL – отслеживать тело, заголовки и метаданные как для запроса, так и для ответа.
Обработка ошибок
Обработчик ошибок Feign по умолчанию – ErrorDecoder.default – всегда генерирует исключение FeignException.
Это поведение не всегда самое полезное. Чтобы настроить выбрасываемое исключение, можно использовать CustomErrorDecoder:
public class CustomErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String methodKey, Response response) {
switch (response.status()){
case 400:
return new BadRequestException();
case 404:
return new NotFoundException();
default:
return new Exception("Generic error");
}
}
}
Затем необходимо заменить ErrorDecoder по умолчанию, добавив bean-компонент в класс @Configuration:
@Configuration
public class ClientConfiguration {
@Bean
public ErrorDecoder errorDecoder() {
return new CustomErrorDecoder();
}
}
Заключение
В этой статье мы обсудили Spring Cloud OpenFeign и его реализацию на простом примере приложения.
Мы также узнали, как настраивать клиент, добавлять перехватчики к запросам и обрабатывать ошибки с помощью Hystrix и ErrorDecoder.
Все примеры кода из данной статьи доступны на GitHub.