Skip to content

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获取服务地址了;valuename必填;

然后就可以调用第三方接口:

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);

}