SpringCloud 之OpenFeign服务消费者

本文深入探讨Feign微服务调用机制,涵盖OpenFeign与Ribbon的区别,Feign快速入门,超时控制,日志打印,工作原理,以及如何在Feign中使用HttpClient和OkHttp。

Feign简介

官网地址:https://github.com/OpenFeign/feign

Feign是一个声明式的Web服务客户端,让编写Web服务客户端变得非常容易,只需 创建一个接口并在接口上添加注解即可。
在这里插入图片描述
Feign默认集成了Ribbon,并和Eureka结合,默认实现了负载均衡的效果。

  • Feign 采用的是基于接口的注解
  • Feign 整合了ribbon,具有负载均衡的能力
  • 整合了Hystrix,具有熔断的能力

OpenFeign简介

Spring Cloud提供的Feign增强版,使得Feign支持Spring MVC的注解,开发更容易了。

OpenFeign快速入门

创建一个model工程作为服务消费者,即eureka-feign-client
导入依赖

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

在启动类中添加 @EnableFeignClients 注解开启Feign的功能。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableEurekaClient
// 开启Feign的功能
@EnableFeignClients
public class EurekaFeignClientApplication {

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

配置文件appication.yml

server:
  port: 8083

spring:
  application:
    name: eureka-feign-client

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8100/eureka/

定义一个Feign接口,在接口上加@FeignClient注解来声明一个 Feign Client,
其中value为远程调用其他服务的服务名。

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

// 申明这是一个Feign客户端,并且指明服务id
@FeignClient(value = "eureka-client")
public interface FeignClientInter {

    // 这里定义了类似于SpringMVC用法的方法,就可以进行RESTful方式的调用了
    @GetMapping(value = "/hello")
    String sayHelloFromClient();
}

通过上面定义的Feign客户端来消费服务。

import com.yq.feign.service.FeignClientInter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @Autowired(required = false)
    FeignClientInter feignClientInter;

    @GetMapping("/feign")
    public String demo() {
        return feignClientInter.sayHelloFromClient();
    }
}

启动测试
在浏览器上多次访问http://127.0.0.1:8083/feign,浏览器交替显示,说明集成了Ribbon负载均衡。
在这里插入图片描述
在这里插入图片描述

OpenFeign超时控制

OpenFeign默认等待1秒钟。超过后报错。
在这里插入图片描述
超时配置:

# 设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
  # 指的是建立连接所用的时间,适用于网络状态正常的情况下,两端连接所用的时间
  ReadTimeout: 5000
  # 指的是建立连接后从服务器读取到可用资源所用的时间
  ConnectTimeout: 5000

OpenFeign日志打印功能

在这里插入图片描述
日志级别:
在这里插入图片描述
配置日志Bean:

import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignConfig {
    @Bean
    public Logger.Level feignLoggerLevel() {
        // 请求和响应的头信息,请求和响应的正文及元数据
        return Logger.Level.FULL;
    }
}

YML文件里需要开启日志的Feign客户端:

logging:
  level:
    # feign日志以什么级别监控哪个接口
    com.demo.service.PaymentFeignService: debug

后台查看日志:
在这里插入图片描述

Feign的工作原理

Feign 是一个伪 Java Http 客户端 , Feign 不做任何的请求处理。 Feign 通过处理注解生成 Request 模板,从而简化了 Http API 的开发。
开发人员可以使用注解的方式定制 Request API 模板。在发送 HttpRequest 请求之前 , Feign 通过处理注解的方式替换掉 Request 模板中的参数,生成真正的 Request ,并交给 Java Http 客户端去处理 。利用这种方式,开发者只需要关注 Feign 注解模板的开发 ,而不用关注 Http 请求本身,简化了 Http 请求的过程 ,使得 Http 请求变得简单和容易理解。

  • Feign 通过包扫描注入 FeignClient 的 Bean ,该源码在 FeignClientsRegistrar 类中 。首先在程序启动时,会检查是否有@EnableFeignClients 注解,如果有该注解,则开启包扫描,扫描被@FeignClient 注解的接口 。
    在这里插入图片描述

  • 当程序的启动类上有@EnableFeignClients注解。在程序启动后,程序会通过包扫描将有@FeignClient 注解修饰的接口得到一个BeanDefinition ,并将BeanDefinition 注入 IoC 容器中。org.springframework.cloud.openfeign.FeignClientsRegistrar#registerFeignClients

  • 通过 JDK 的代理,当调用 FeignClient 接口里面的方法时,该
    方法会被拦截,源码在 ReflectiveFeign 类。
    在这里插入图片描述

  • SynchronousMethodHandler 类进行拦截处理,会根据参数生成RequestTemplate 的Http请求模板对象,然后调用 executeAndDecode()方法,该方法通过 RequestTemplate 生成 Request请求对象,然后用 HttpClient 获取 Response ,即通过 HttpClient 进行 Http 请求来获取响应。
    在这里插入图片描述
    最终通过Client的实现类LoadBalancerFeignClient ,即负载均衡客户端,来实现负载均衡请求调用服务的。

在Feign中使用HttpClient和OkHttp

首先查看 FeignRibbonClient 的自动配置类 FeignRibbonClientAutoConfiguration ,@Import 自动注入三个请求框架,从名字上可以看出默认使用最后一个Client。

HttpClientFeignLoadBalancedConfiguration
OkHttpFeignLoadBalancedConfiguration
DefaultFeignLoadBalancedConfiguration

在这里插入图片描述
查看DefaultFeignLoadBalancedConfiguration源码,通过new Client.Default()来创建Client的。
在这里插入图片描述
实现类是 Client.Default,,Client.Default 是由 HttpURLConnnection 来实现网络请求的。
在这里插入图片描述
继续分析@Import导入的类,FeignClient 还支持 HttpClient 和 OkhHttp 来进行网络请求,那么 Feign 中如何选择网络请求框架呢?
下面继续查看HttpClientFeignLoadBalancedConfigurationOkHttpFeignLoadBalancedConfiguration源码。
在这里插入图片描述
在这里插入图片描述
从代码@ConditionalOnClass(ApacheHttpClient.class)@ConditionalOnClass(OkHttpClient.class)注解可知道,只需要在 POM 文件加上HttpClient或者OkHttpClient 的 Classpath 即可 。
另外需要在配置文件 application.yml 中配置feign.httpclient.enabled或者feign.okhttp.enabled为 true,从 HttpClientFeignLoadBalancedConfiguration类上的@ConditionalOnProperty注解可知,这个配置可以不写,因为在默认的情况下就为true,如果使用OkHttp,必须配置feign.okhttp.enabled,因为OkHttpFeignLoadBalancedConfiguration类上的@ConditionalOnProperty注解未设置为true。

POM 文件加上 feign-httpclient 的依赖, Feign 就会采用 HttpClient 作为网络请求框架。

<dependency>
    <groupId>com.netflix.feign</groupId>
    <artifactId>feign-okhttp</artifactId>
    <version>RELEASE</version>
</dependency>

POM 文件加上 feign-okhttp 的依赖, Feign 就会采用 OkHttp 作为网络请求框架。

<dependency>
    <groupId>com.netflix.feign</groupId>
    <artifactId>feign-httpclient</artifactId>
    <version>RELEASE</version>
</dependency>

如果容器中三个Client都存在,Feign会默认根据@Import注入类的顺序来选择Client。在LoadBalancerFeignClient类中可以看出使用了哪个Client。
在这里插入图片描述

Feign调用链

在这里插入图片描述

OpenFeign与Ribbon区别

Ribbon侧重于做服务调用时的负载均衡,而OpenFeign侧重于面向接口进行服务调用。

在只引入Ribbon依赖的时候,可以使用restTemplate来进行服务调用,大概流程如下:
在这里插入图片描述

OpenFeign相比Ribbon在代码实现上是在客户端多了一层接口,之前用Ribbon的时候客户端只有Controller层,通过RestTemplate请求服务端的Controller层。

Openfeign需要在客户端创建一个service层,并创建一个service接口(要用到@FeignClient注解),其方法和服务端的Controller里的方法相对应,之后客户端的Controller调这个接口就行了。

OpenFeign的引入直接砍掉了RestTemplate,客户端Controller在调用服务端时不需要再关注请求的方式、地址以及是forObject还是forEntity,完全面向接口调用,层次结构更加明了,而且OpenFeign自身集成Ribbon,所以默认开启轮询的负载均衡。而且还可以和Hystrix相结合,写一个类实现service接口,其中实现的方法的方法体便是降级或熔断的fallback方法(需要在接口中指定该实现类)。这样结构更清晰,耦合也更低。

总结如下

1、通过 @EnableFeignCleints 注解启动 Feign Starter 组件。
2、Feign Starter 在项目启动过程中注册全局配置,扫描包下所有的 @FeignClient 接口类,并进行注册 IOC 容器。
3、@FeignClient 接口类被注入时,通过 FactoryBean#getObject 返回动态代理类。
4、接口被调用时被动态代理类逻辑拦截,将 @FeignClient 请求信息通过编码器生成 Request(没有带上Header头)。
5、交由 Ribbon 进行负载均衡(默认轮询的方式),挑选出一个健康的 Server 实例。
6、继而通过 Client 携带 Request 调用远端服务返回请求响应。
7、通过解码器生成 Response 返回客户端,将信息流解析成为接口返回数据。

掌握 OpenFeign 核心原理

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值