Loading

Feign

Feign是为了解决RestTemplate将URL请求卷入到开发业务的问题而设计的。

使用Feign,你可以针对一个服务创建一个接口,其中描述该服务的细节,然后就可以通过这个接口来对服务进行调用,而非用URL拼接的方式。

QuickStart

导入

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

启动类添加注解:

@EnableFeignClients
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

定义服务接口

@FeignClient(name = "user-service") // 指定服务名
public interface UserClient {
    @GetMapping("/user/{id}")  // 以Controller类似的方式开发服务接口
    User findById(@PathVariable("id") Integer id);
}

调用服务接口

下面是使用userClientrestTemplate的对比,清爽多了:

-@Autowired
-private RestTemplate restTemplate;

+@Autowired
+private UserClient userClient;

public Order getOrderById(Integer id) {
    Order order = repository.selectById(id);
-    User user = restTemplate.getForObject("http://user-service/user/" + order.getUserId(), User.class);
+    User user = userClient.findById(order.getUserId());
    order.setUser(user);
    return order;
}

负载均衡

使用RestTemplate时我们可以在RestTemplateBean上加一个LoadBalance注解,这样这个RestTemplate发出的请求就具有了负载均衡能力,Feign不用做任何配置就已经有了负载均衡能力,也是使用Ribbon实现的

img

自定义配置

Feign提供如下自定义配置:

img

配置文件

feign:
  client:
    config:
      # 配置user-service的日志级别为HEADERS,即打印出请求响应的头信息
      # 如果你想提供一些默认的全局配置,可以使用default
      user-service:
        logger-level: HEADERS


# 同时别忘了,将你的包的日志级别开成debug
logging:
  level:
    top:
      yudoge: debug

打印出的日志:

img

Java代码

public class FeignClientConfiguration {
    @Bean
    public Logger.Level feignLogLevel() {
        return Logger.Level.FULL;
    }
}

现在,你定义了一个Feign客户端配置类,如果你期望这个配置类里的配置作用于全局,就在全局的@EnableFeignClients注解上添加

@SpringBootApplication
// 指定配置类
@EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class)
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }

}

如果你想要它应用在特定的服务上,就在服务的@FeignClient上添加:

@FeignClient(name = "user-service", configuration = FeignClientConfiguration.class)
public interface UserClient {
    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Integer id);
}

日志对于Feign性能的影响较大,能不开就不开

Feign替换客户端实现

Feign提供三种客户端实现:

  1. URLConnection:默认实现,不支持连接池
  2. Apache HttpClient:支持连接池
  3. OKHttp:支持连接池

下面替换客户端实现到HttpClient

导入依赖

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>

配置连接池

feign:
  client:
    config:
      default:
        logger-level: BASIC
  httpclient:
    enabled: true
    max-connections: 200
    max-connections-per-route: 50

实际上,我发现Feign默认已经带这个httpclient依赖了,而且它默认也是开启状态,可能现在Feign的默认客户端就是它吧,所以说我们不配置应该也是一样的。

Feign最佳实践

现在有一个问题,我们把UserClientUser这个实体类定义在了order-service微服务中,因为它要使用user-service,这看起来理所当然。但当我们有另一个服务业要使用user-service时,我们就得将这一套相同的东西再在另一个微服务中定义一次。

img

再有一个问题就是,我们定义的FeignClient和要调用的微服务的Controller极其类似:

服务消费者中的Client:

img

服务提供者中的Controller:
img

我们应该消除这两种重复

消除消费者Client和提供者Controller中的方法签名重复

在一个公共项目中提供一个父接口,让客户端的Client直接继承这个接口,提供者直接实现这个接口

img

问题:消费者和提供者紧耦合到同一个API,并且在SpringMVC中,方法注解信息不能继承,所以说这些东西你还得重新写。

消除使用同一个服务的多个Client端中的代码重复

在一个公共项目中提供客户端需要的Client、Pojo以及客户端配置,让需要的用户直接引入后使用

img

总结

  1. Feign相比RestTemplate,使用Client接口方式编程,让我们的代码更加优雅
  2. Feign自带使用Ribbon的负载均衡
  3. Feign提供一些自定义的配置,最常用的就是配置日志
  4. Feign底层http请求客户端可以被替换
  5. 我们可以通过单独将多个消费者都需要的Client、配置和Pojo放在单独的项目中以消除代码在多个服务消费者间的重复编写
posted @ 2022-08-06 11:59  yudoge  阅读(53)  评论(0编辑  收藏  举报