Appearance
Spring Cloud OpenFeign
本文介绍Spring Cloud OpenFeign的用法。
1. 介绍
OpenFeign 是一个 声明式、模板化的 HTTP 客户端。简单来说,它能以接口和注解的形式来定义需要调用的 HTTP 服务,然后 Feign 会完成底层复杂的 HTTP 请求构建、发送、接收和响应解析等工作。
而我们之前通过RestTemplate调用HTTP接口,称为编程式HTTP客户端。
2. 调用微服务
本小节介绍如何使用OpenFeign调用其他微服务。
2.1 引入依赖
首先在pom.xml中引入OpenFeign依赖:
xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>注意,OpenFeign需要和LoadBalancer配合使用。
xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>2.2 编写接口
在order服务中编写OpenFeign接口:
java
@FeignClient(value = "product-service")
public interface ProductFeignClient {
@GetMapping("/product/{id}")
Product getProduct(@PathVariable("id") Long id);
}@FeignClient:value为服务名,即在Nacos中进行注册的服务名;- 接口中的方法即product服务暴露的方法,可以参照product服务中Controller接口方法签名;
2.3 调用接口
当定义了服务调用接口后,就可以直接使用:
java
@RestController
public class OrderController {
@Resource
private ProductFeignClient productFeignClient;
@GetMapping("/getProductFromOrder/{productId}")
public Product getgetProductFromOrderOrder(@PathVariable("productId") Long id){
Product product = productFeignClient.getProduct(id);
return product;
}
}3. 调用第三方接口
除了使用OpenFeign调用微服务,也可以调用其他第三方接口。
例如,我们可以调用第三方接口,随机输出获取一张狗狗的照片:https://dog.ceo/dog-api/
定义接口如下:
java
@FeignClient(value = "dog-service", url = "https://dog.ceo/api")
public interface DogApiFeignClient {
@GetMapping("/breeds/image/random")
String randomDog();
}- 在
@FeignClient中,使用url指定第三方地址,这样就不会去Nacos获取服务地址了;value或name必填;
然后就可以调用第三方接口:
java
@RestController
public class DogController {
@Resource
private DogApiFeignClient uomgApiFeignClient;
@GetMapping("/randomDog")
public String randomDog(){
String dog = uomgApiFeignClient.randomDog();
return dog;
}
}4. 其他设置
4.1 日志设置
我们可以设置打印日志级别,以显示HTTP请求与响应的完整信息。
首先在配置文件中设置Feign客户端的日志级别:
properties
logging.level.org.example.feign=debug
# org.example.feign 是 Feign客户端所在的包然后往容器中添加如下组件:
java
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}4.2 超时设置
在Feign中,有两种超时时间可以设置:
connectTimeout:连接超时,如果目标服务器不可达或目标服务未启动,则会导致 连接超时,默认时间为10秒;readTimeout:读取超时,是指连接后到接收到响应的时间,如果目标服务耗费过多时间处理业务,则会导致读取超时,默认时间为60秒;
在配置文件中,可以配置默认的超时时间,也可以为指定的Feign客户端单独配置。默认时间单位为毫秒。
properties
# 默认的超时时间设置
spring.cloud.openfeign.client.config.default.connect-timeout=1000
spring.cloud.openfeign.client.config.default.read-timeout=1000
# 指定Feign客户端的超时时间设置
spring.cloud.openfeign.client.config.product-service.connect-timeout=2000
spring.cloud.openfeign.client.config.product-service.read-timeout=5000当需要为指定的Feign客户端进行设置时,例如product-service,应该指明@FeignClient中的value值;
4.3 重试机制
默认情况下,如果Feign发送的Http请求失败了,不会进行重试。
我们可以配置重试机制,只需要在容器中添加Retryer组件,在OpenFeign中提供了一个默认实现Retryer.Default
java
@Bean
Retryer retryer(){
return new Retryer.Default(100L, TimeUnit.SECONDS.toMillis(1L), 5);
}- 第一个参数:是指重试间隔初始时间,即一个请求失败后,需要间隔多长时间,再次发送下一个请求。如果有多次失败,那么每次的重试间隔时间需要乘以1.5,即逐渐增大重试间隔时间。
- 第二个参数:最大的重试间隔时间,由于每次重试都会增大间隔时间,为了防止长时间等待重试,所以设置一个最大的重试间隔时间;
- 第三个重试:请求次数,即第一次发送的请求加上重试次数;
4.4 拦截器
OpenFeign提供了请求和响应的拦截器:
RequestInterceptor:请求拦截器;ResponseInterceptor:响应拦截器;
这里以请求拦截器为例,在请求头中增加Token:
java
@Component
public class TokenHeaderInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
template.header("Token", UUID.randomUUID().toString());
}
}只需要将拦截器加入到容器中即可生效。
4.5 Fallback
Fallback是指兜底返回,是指请求出错时,返回一些默认数据、缓存数据等,而不是直接报错。
Fallback需要配合sentinel一起使用。
Fallback实现步骤如下:
- 在pom.xml中引入sentinel;
- 在配置文件中开启sentinel支持;
- 实现FeignClient客户端接口;
- 在FeignClient客户端接口注解
@FeignClient中指明fallback;
首先引入依赖sentinel:
xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>然后在配置文件中开启sentinel支持:
properties
feign.sentinel.enabled=true编写Fallback实现类,将其加入到容器中:
java
@Component
public class ProductFeignClientFallback implements ProductFeignClient {
@Override
public Product getProduct(Long id) {
Product product = new Product();
product.setId(id);
product.setName("默认商品");
product.setPrice(new BigDecimal("0"));
product.setStock(0);
return product;
}
}最后在@FeignClient中指明fallback:
java
@FeignClient(value = "product-service", fallback = ProductFeignClientFallback.class)
public interface ProductFeignClient {
@GetMapping("/product/{id}")
Product getProduct(@PathVariable("id") Long id);
}