openFeign简介

OpenFeign是一种声明式、模板化的HTTP客户端。在Spring Cloud中使用OpenFeign,可以做到使用HTTP请求访问远程服务,就像调用本地方法一样的,开发者完全感知不到这是在调用远程方法,更感知不到在访问HTTP请求。

openFeign与Ribbon的区别

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

OpenFeign相比Ribbon在代码实现上是在客户端多了一层接口,之前用ribbon的时候客户端只有controller层,通过restTemplate请求服务端的controller层。Openfeign需要在客户端创建一个service层,并创建一个service接口(要用到@FeignClient注解),其方法和服务端的controller里的方法相对应,之后客户端的controller调这个接口就行了。

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

openFeign的工作原理

  1. SpringBoot 应用启动时, 由针对 @EnableFeignClient 这一注解的处理逻辑触发程序扫描 classPath中所有被@FeignClient 注解的类, 这里以 DemoService 为例, 将这些类解析为 BeanDefinition 注册到 Spring 容器中
  2. Sping 容器在为某些用的 Feign 接口的 Bean 注入 DemoService 时, Spring 会尝试从容器中查找 DemoService 的实现类
  3. 由于我们从来没有编写过 DemoService 的实现类, 上面步骤获取到的 DemoService 的实现类必然是 feign 框架通过扩展 spring 的 Bean 处理逻辑, 为 DemoService 创建一个动态接口代理对象, 这里我们将其称为 DemoServiceProxy 注册到spring 容器中。
  4. Spring 最终在使用到 DemoService 的 Bean 中注入了 DemoServiceProxy 这一实例。
  5. 当业务请求真实发生时, 对于 DemoService 的调用被统一转发到了由 Feign 框架实现的 InvocationHandler 中, InvocationHandler 负责将接口中的入参转换为 HTTP 的形式, 发到服务端, 最后再解析 HTTP 响应, 将结果转换为 Java 对象, 予以返回。

上面整个流程可以进一步简化理解为:

  1. 我们定义的接口 DemoService 由于添加了注解 @FeignClient, 最终产生了一个虚假的实现类代理
  2. 使用这个接口的地方, 最终拿到的都是一个假的代理实现类 DemoServiceProxy
  3. 所有发生在 DemoServiceProxy 上的调用, 都被转交给 Feign 框架, 翻译成 HTTP 的形式发送出去, 并得到返回结果, 再翻译回接口定义的返回值形式。

所以不难发现, Feign 的核心实现原理就是java 原生支持的基于接口的动态代理

openFeign在项目中的应用

  1. 添加了 Spring Cloud OpenFeign 的依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  1. 在 SpringBoot 启动类上添加了注解 @EnableFeignCleints
package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableFeignClients
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }
}
  1. 按照 Feign 的规则定义接口 PaymentFeignService, 添加@FeignClient 注解 ``` package com.atguigu.springcloud.service;

import com.atguigu.springcloud.entities.CommonResult; import com.atguigu.springcloud.entities.Payment; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable;

@Component @FeignClient(value = “CLOUD-PAYMENT-SERVICE”) public interface PaymentFeignService {

@GetMapping(value="/payment/{id}")
public CommonResult<Payment> getpaymentById(@PathVariable("id") Long id);

@GetMapping(value="/payment/feign/timeout")
public String paymentFeifnTimeout();

}



4. 
在需要使用 Feign 接口 PaymentFeignService的地方, 直接利用[@Autowire ](/Autowire ) 进行注入

package com.atguigu.springcloud.controller;

import com.atguigu.springcloud.entities.CommonResult; import com.atguigu.springcloud.entities.Payment; import com.atguigu.springcloud.service.PaymentFeignService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController @Slf4j public class OrderfeignController { @Autowired private PaymentFeignService paymentFeignService;

@GetMapping(value="/consumer/payment/{id}")
public CommonResult<Payment> getpaymentById(@PathVariable Long id){
    return paymentFeignService.getpaymentById(id);
}

@GetMapping(value="/consumer/payment/feign/timeout")
public String paymentFeifnTimeout(){
    return paymentFeignService.paymentFeifnTimeout();
}

}




5.使用接口完成对服务的调用

<a name="cdad1246"></a>
## openFeign超时控制

feign默认的等待时间是1s,如果有特殊需求需要延长,则要在配置文件中设置ribbon的等待时间

ribbon: ReadTimeout: 5000 ConnectTimeout: 5000


<a name="96342901"></a>
## openFeign日志控制

<a name="ea7efa81"></a>
#### feign的日志级别

NONE: 默认的,不显示任何日志

BASIC: 仅记录请求方法、URL、响应状态码以及执行时间

HEADERS:除了BASIC 中自定义的信息外,还有请求和响应的信息头

FULL: 除了HEADERS中定义的信息外, 还有请求和响应的正文以及元数据。

<a name="5c0e91d0"></a>
#### 配置feignConfig的日志bean(logger是feign的logger)

package com.atguigu.springcloud.config;

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

@Configuration public class FeignConfig {

@Bean
Logger.Level feignLoggerLevel(){
    return Logger.Level.FULL;
}

} ```