Feign英文表意为“假装,伪装,变形” ,是一个http请求调用的轻量级框架,属于NetFlix公司,和其他的NetFlix组件一样,已经宣布不再进行更新了。因此Spring社区在Feign的基础之上推出了OpenFeign, 对Feign进行增强支持Spring MVC注解,可以像Spring Web一样使用HttpMessageConverters等。 本质上来讲,二者都是完成服务调用的轻量级框架,使用方法也一致。
在服务消费者中使用Feign,可以做到使用HTTP请求访问远程服务,就像调用本地方法一样的,开发者完全感知不到这是在调用远程方法,更感知不到在访问HTTP请求。
第一步:pom依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
第二步:主启动类
@SpringBootApplication
//开启Feign功能支持
@EnableFeignClients
public class OrderFeignMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderFeignMain80.class,args);
}
}
第三步:书写服务接口
//表示当前FeignClient消费的服务是CLOUD-PAYMENT-SERVICE(Eureka上的服务名)
@FeignClient("CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService {
//服务提供者暴露出来的uri路径和访问方式
@GetMapping(value = "/payment/get/{id}")
//服务提供者暴露出来的方法声明
CommonResult getPaymentById(@PathVariable("id") Long id);
}
第四步:书写控制器
@RestController
public class OrderController {
//自动装配服务接口
@Autowired
private PaymentFeignService paymentFeignService;
//当用户访问此路径时,调用自己的服务接口
//服务接口因为使用了@FeignClient注解,所以会根据配置请求:
//http://CLOUD-PAYMENT-SERVICE/payment/get/{id}
//因为Feign集成了Ribbon,Ribbon会做负载均衡,并且将服务名转化成真实的url地址完成调用
@GetMapping("/consumer/payment/get/{id}")
public CommonResult getPaymentById(@PathVariable Long id){
return paymentFeignService.getPaymentById(id);
}
}
相同点:
1、本质上讲,二者都是用来向服务提供者发送http请求的工具
2、二者本身都不具备负载均衡的能力
3、向服务端发送请求时都是使用的服务名而非真实ip+port
4、都是利用了Ribbon完成的负载均衡,地址转换等工作。
5、默认情况下,都是采用轮询的方式完成负载均衡
不同点:
1、OpenFeign使用的是动态代理技术,RestTemplate使用的是拦截器技术。
2、使用的注解不同,OpenFeign使用的是@EnableFeignClients和@FeigeClient;RestTemplate使用的是@LoadBalanced;
3、OpenFeign本身已经继承了NetFlix Ribbon(并非Spring Cloud Ribbon),直接就可以使用Ribbon的负载均衡功能;RestTemplate只是Spring推出的一个类库,想要使用Ribbon的负载均衡功能,必须另外引入Spring Cloud Ribbon的依赖(经常是由Eureka间接引入的)。
优缺点对比:
1、 在使用RestTemplate来调用接口服务时,需要向方法传入服务提供者的接口地址,当接口地址较多时,不太方便管理。而OpenFeign通过一个服务接口把所有要调用的接口地址集中管理起来,这是一个比较好的管理方式。
2、使用OpenFeign进行服务调用,我们不需要像使用RestTemplate那样关心调用哪一个方法、参数类型和返回值等问题,使用起来更加方便。
3、OpenFeign是面向接口的编程方式,更加符合程序员的习惯,类似于三层架构中的controller调用service。
所以实际开发当中,使用OpenFeign完成服务调用的场景比使用RestTemplate的场景要多得多。
因为OpenFeign和RestTemplate一样,都是和Ribbon搭配使用的,只不过一个采用了动态代理的方式,一个采用了拦截器的方式,最终负载均衡的工作都是交给Ribbon来做的,所以替换Ribbon默认负载均衡策略的方式也是一样的。如下:
默认情况下,自动装配好的RestTemplate使用的负载均衡策略是轮询,即根据服务清单,一次访问各个服务实例。而Ribbon给我们提供的负载均衡策略不止轮询这一种,还有一些其他的策略:
第一步:创建配置类MySelfRuler
@Configuration
public class MySelfRuler {
@Bean
public IRule mySelfRule(){
//将默认的轮询算法替换成随机算法
//此处可以使用其他官方提供的算法,也可以自定义算法
return new RandomRule();
}
}
第二步:在服务消费者主配置类上使用@RibbonClient注解
@SpringBootApplication
@EnableEurekaClient
//指明本服务是一个消费者,消费CLOUD-PAYMENT-SERVICE
//使用的配置为自定义配置MySelfRuler
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MySelfRuler.class)
public class OrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderMain80.class,args);
}
}
但是要注意的是:
官方给出明确警告,自定义的规则配置类不能@ComponentScan注解所在包及其子包下,否则自定义的配置类会被所有的Ribbon客户端共享,不能够起到服务特殊化定制的目的。对于单个的SpringBoot程序来讲,MySelfRuler不能放在主启动类所在包及其子包下。
默认情况下,Feign发送的请求超时时间为1s,但是我们在实际开发中,可能有一些服务本身正常的调用时间就比较长,为了防止程序报错,针对这些这些服务的消费者,我们需要修改feign默认的超时时间,因为feign默认集成了Ribbon,我们可以通过设置Ribbon的超时时间来进行Feign的超时设置。如下:
ribbon:
#连接成功后读取资源的超时时间(ms)
ReadTimeout: 5000
#建立连接的超时时间(ms)
ConnectTimeout: 5000
Feign与RestTemplate不同的是,http请求详细信息封装的更深了,为了方便我们查看每一次发送的http请求信息详细信息,Feign给我们提供了日志功能。说白了就是对Feign接口的调用情况进行监控和输出。
日志级别:
指http请求信息的详细程度,分为四个等级:
NONE:默认,不显示任何日志
BASIC:仅记录请求方法、URL、响应状态码及执行时间
HEADERS:除了BASIC的内容之外,还有请求和响应的头信息
FULL:除了HEADERS的内容外,还有请求和响应的正文以及元数据。
OpenFeign使用的是slf4j作为日志门面,日志的实现根据整个项目确定。不同的日志级别底层采用的都是debug级别的输出,只不过不同的日志级别对应的输出内容数量不同。
如何修改Feign的日志级别:
第一步:书写一个配置类,向容器中注入日志级别的bean
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel(){
//将默认的日志级别改为FULL
return Logger.Level.FULL;
}
}
第二步:在配置文件中指明Feign接口日志的输出级别,必须为debug,因为Feign底层使用的是debug进行的日志输出。如果不指定,将使用全局统一的日志级别。
logging:
level:
com.ly.springcloud.service.PaymentFeignService: debug
第三步:测试,使用Feign完成服务调用,控制台得到如下日志:(也可以根据日志配置,将此内容输出到配置文件中去)
[PaymentFeignService#getPaymentById] <--- HTTP/1.1 200 (291ms)
[PaymentFeignService#getPaymentById] connection: keep-alive
[PaymentFeignService#getPaymentById] content-type: application/json
[PaymentFeignService#getPaymentById] date: Fri, 31 Jul 2020 03:49:36 GMT
[PaymentFeignService#getPaymentById] keep-alive: timeout=60
[PaymentFeignService#getPaymentById] transfer-encoding: chunked
[PaymentFeignService#getPaymentById]
[PaymentFeignService#getPaymentById] {"code":200,"message":"查询成功,port: 8001","data":{"id":2,"serial":"AAAAAAAAA"}}
[PaymentFeignService#getPaymentById] <--- END HTTP (85-byte body)