openFeign简介
OpenFeign是一种声明式、模板化的HTTP客户端。在Spring Cloud中使用OpenFeign,可以做到使用HTTP请求访问远程服务,就像调用本地方法一样的,开发者完全感知不到这是在调用远程方法,更感知不到在访问HTTP请求。
openFeign与Ribbon的区别
Ribbon侧重于做服务调用时的负载均衡,而OpenFeign侧重于面向接口进行服务调用。
OpenFeign相比Ribbon在代码实现上是在客户端多了一层接口,之前用ribbon的时候客户端只有controller层,通过restTemplate请求服务端的controller层。Openfeign需要在客户端创建一个service层,并创建一个service接口(要用到@FeignClient注解),其方法和服务端的controller里的方法相对应,之后客户端的controller调这个接口就行了。
OpenFeign的引入直接砍掉了restTemplate,客户端controller在调用服务端时不需要再关注请求的方式、地址以及是forObject还是forEntity,完全面向接口调用,层次结构更加明了,而且OpenFeign自身集成Ribbon,所以默认开启轮询的负载均衡。(虽然Eureka也集成了ribbon,但大概是eureka已经停更了,所以openFeign集成了ribbon)。而且还可以和hystrix相结合,写一个类实现service接口,其中实现的方法的方法体便是降级或熔断的fallback方法(需要在接口中指定该实现类)。这样结构更清晰,耦合也更低。
openFeign的工作原理
- SpringBoot 应用启动时, 由针对
@EnableFeignClient这一注解的处理逻辑触发程序扫描 classPath中所有被@FeignClient注解的类, 这里以DemoService为例, 将这些类解析为 BeanDefinition 注册到 Spring 容器中 - Sping 容器在为某些用的 Feign 接口的 Bean 注入
DemoService时, Spring 会尝试从容器中查找 DemoService 的实现类 - 由于我们从来没有编写过
DemoService的实现类, 上面步骤获取到的 DemoService 的实现类必然是 feign 框架通过扩展 spring 的 Bean 处理逻辑, 为DemoService创建一个动态接口代理对象, 这里我们将其称为DemoServiceProxy注册到spring 容器中。 - Spring 最终在使用到
DemoService的 Bean 中注入了DemoServiceProxy这一实例。 - 当业务请求真实发生时, 对于
DemoService的调用被统一转发到了由 Feign 框架实现的InvocationHandler中,InvocationHandler负责将接口中的入参转换为 HTTP 的形式, 发到服务端, 最后再解析 HTTP 响应, 将结果转换为 Java 对象, 予以返回。
上面整个流程可以进一步简化理解为:
- 我们定义的接口
DemoService由于添加了注解@FeignClient, 最终产生了一个虚假的实现类代理 - 使用这个接口的地方, 最终拿到的都是一个假的代理实现类
DemoServiceProxy - 所有发生在
DemoServiceProxy上的调用, 都被转交给 Feign 框架, 翻译成 HTTP 的形式发送出去, 并得到返回结果, 再翻译回接口定义的返回值形式。
所以不难发现, Feign 的核心实现原理就是java 原生支持的基于接口的动态代理
openFeign在项目中的应用
- 添加了 Spring Cloud OpenFeign 的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 在 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);
}
}
- 按照 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;
}
} ```
