Spring Cloud OpenFeign 入门
1、概览
本文将带你了解 Spring Boot 应用中的声明式 REST 客户端 - Spring Cloud OpenFeign。
Feign 通过可插拔的注解支持(包括 Feign 注解和 JAX-RS 注解)使编写 Web 客户端更加容易。
此外,Spring Cloud 还增加了对 Spring MVC 注解和使用与 Spring Web 中相同的 HttpMessageConverter
的支持。
使用 Feign 的一个好处是,除了接口定义外,无需编写任何调用服务的代码。
2、依赖
首先,创建 Spring Boot Web 项目,并在 pom.xml
文件中添加 spring-cloud-starter-openfeign
依赖:
<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>
你可以在 Maven Central 上找到 spring-cloud-starter-openfeign 和 spring-cloud-dependencies 的最新版本。
3、Feign 客户端
接着,需要将 @EnableFeignClients
添加到 main 类中:
@SpringBootApplication
@EnableFeignClients
public class ExampleApplication {
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
}
该注解会扫描、加载应用中定义的 Feign 客户端接口组件。
然后,使用 @FeignClient
注解声明一个 Feign 客户端:
@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);
}
本例配置了一个客户端来读取 JSONPlaceholder
API。
@FeignClient
注解中传递的 value
参数是一个强制性的任意客户端名称,而 url
参数则指定了 API 的基本 URL。
此外,由于该接口是一个 Feign 客户端,因此可以使用 Spring Web 注解来声明要调用的 API。
4、配置
每个 Feign 客户端都由一组可定制的组件组成。
Spring Cloud 会使用 FeignClientsConfiguration
类为每个已命名的客户端按需创建一个新的默认设置。
涉及的配置 Bean 如下:
- Decoder -
ResponseEntityDecoder
,它封装了SpringDecoder
,用于解码Response
- Encoder -
SpringEncoder
,用于对RequestBody
进行编码。 - Logger -
Slf4jLogger
,Feign 使用的默认 logger。 - Contract -
SpringMvcContract
,提供注解处理功能 - Feign-Builder -
HystrixFeign.Builder
,用于构建组件 - Client -
LoadBalancerFeignClient
或默认 Feign 客户端
4.1、自定义 Bean 配置
如果想自定义其中一个或多个 Bean,可以通过创建一个 Configuration
类来覆盖它们,然后将其添加到 FeignClient
注解中:
@FeignClient(value = "jplaceholder",
url = "https://jsonplaceholder.typicode.com/",
configuration = ClientConfiguration.class)
public class ClientConfiguration {
@Bean
public OkHttpClient client() {
return new OkHttpClient();
}
}
在本例中,通过配置让 Feign 使用 OkHttpClient
代替默认客户端来支持 HTTP/2。
Feign 支持多种客户端以满足不同的使用情况,其中包括 ApacheHttpClient,它会随请求发送更多的 Header 信息,例如某些服务器所期望的 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>
你可以在 Maven Central 上找到 feign-okhttp 和 feign-httpclient 的最新版本。
4.2、通过 Properties 进行配置
可以使用 application.properties
代替配置类来配置 Feign 客户端。
示例 application.yaml
如下:
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
loggerLevel: basic
如上,将超时时间设置为 5 秒,并将应用中每个已声明客户端的 logger 日志级别设置为 basic 级别。
最后,可以通过默认的客户端名称 default
来配置所有 @FeignClient
对象,也可以为指定名称的 feign 客户端进行配置:
feign:
client:
config:
jplaceholder:
如果同时定义了 Configuration
Bean 和配置属性,配置属性将覆盖 Configuration
Bean 的值。
5、拦截器
Feign 支持拦截器(Interceptor)。
拦截器可为每个 HTTP 请求/响应执行身份认证、日志记录等各种隐式任务。
Spring Cloud OpenFeign 预定义了一些开箱即用的拦截器,当然,也可以实现自己的拦截器。
5.1、实现 RequestInterceptor
实现 RequestInterceptor
接口,创建自定义的拦截器:
@Bean
public RequestInterceptor requestInterceptor() {
return requestTemplate -> {
requestTemplate.header("user", username);
requestTemplate.header("password", password);
requestTemplate.header("Accept", ContentType.APPLICATION_JSON.getMimeType());
};
}
如上,在拦截器中为请求添加了一些额外的 Header。
要将拦截器添加到请求链中,只需将此 Bean 添加到 Configuration
类中,或者如前所述,在 properties 文件中声明即可:
feign:
client:
config:
default:
requestInterceptors:
com.baeldung.cloud.openfeign.JSONPlaceHolderInterceptor
5.2、使用 BasicAuthRequestInterceptor
或者,可以使用 Spring Cloud OpenFeign 提供的 BasicAuthRequestInterceptor
类:
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("username", "password");
}
就这么简单。现在,所有请求都将包含 Basic Authentication Header。
6、支持 Hystrix
Feign 支持 Hystrix,以实现 fallback 模式。
利用 fallback 模式,当远程服务调用失败时,服务消费者不会产生异常,而是会执行执行 fallback 代码,尝试通过其他方式完成操作。
首先,需要在 properties 文件中添加 feign.hystrix.enabled=true
来启用 Hystrix,这样,就可以实现在服务失败时调用的 fallback 方法:
定义 fallback 实现:
@Component
public class JSONPlaceHolderFallback implements JSONPlaceHolderClient {
@Override
public List<Post> getPosts() {
return Collections.emptyList();
}
@Override
public Post getPostById(Long postId) {
return null;
}
}
然后,在 @FeignClient
注解中设置 fallback
类:
@FeignClient(value = "jplaceholder",
url = "https://jsonplaceholder.typicode.com/",
fallback = JSONPlaceHolderFallback.class)
public interface JSONPlaceHolderClient {
// API
}
7、日志
默认情况下,会为每个 Feign 客户端创建一个 logger。
要启用日志,应在 application.properties
文件中使用客户端接口的包名来声明日志级别:
logging.level.com.baeldung.cloud.openfeign.client: DEBUG
也可以通过完整的客户端接口类名来设置特定客户端的日志级别:
logging.level.com.baeldung.cloud.openfeign.client.JSONPlaceHolderClient: DEBUG
可以为每个客户端配置 Logger.Level
Bean 来表示日志级别:
public class ClientConfiguration {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.BASIC;
}
}
有四种日志级别可供选择:
NONE
:不记录日志,这是默认设置BASIC
:只记录请求方法、URL 和响应状态码HEADERS
:记录基本信息以及请求和响应头FULL
:记录请求和响应的 Body、Header 和元数据
8、异常处理
Feign 默认的异常处理器 ErrorDecoder.default
总是抛出 FeignException
。
要自定义抛出的异常,可以实现 ErrorDecoder
接口来自定义异常处理器:
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");
}
}
}
然后,就像之前那样,在 Configuration
类中添加一个 Bean 来替换默认的 ErrorDecoder
:
public class ClientConfiguration {
@Bean
public ErrorDecoder errorDecoder() {
return new CustomErrorDecoder();
}
}
9、总结
本文介绍了如何使用 Spring Cloud OpenFeign,介绍了如何进行自定义配置、配置拦截器、配置 Hystrix、配置日志、自定义异常处理等等。
Ref:https://www.baeldung.com/spring-cloud-openfeign