Feign是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单。它的使用方法是定义一个服务接口然后在上面添加注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以和Eureka和Ribbon组合使用以支持负载均衡。
Feign能做什么
Feign旨在使编写java Http客户端变得更容易。
前面使用了Ribbon+RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模板化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步封装,由它来帮助我们定义和实现依赖服务接口的定义。在Feign的实现下,我们只需要创建一个接口并使用注解的方式来配置它(一个微服务接口上面标注一个Feign注解即可),即可完成对服务提供方的接口绑定,简化了使用Spring Cloud Ribbon时,自动封装服务调用客户端的开发量。
Feign集成了Ribbon
利用Ribbon维护了服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现服务调用。
Feign和OpenFeign的区别
OpenFeign使用
1,新建Module
2,POM文件
<dependencies> <dependency> <groupId>com.company.springcloud2020</groupId> <artifactId>cloud-api-commons</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <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> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-test</artifactId> </dependency> </dependencies>
3,YML文件
server:
port: 84
spring:
application:
name: cloud-consumer-service
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
instance:
instance-id: order84
prefer-ip-address: true
3,主启动类
@SpringBootApplication @EnableFeignClients public class OpenFeignOrder84 { public static void main(String[] args) { SpringApplication.run(OpenFeignOrder84.class,args); } }
4,业务类
@Component @FeignClient(value = "CLOUD-PAYMENT-SERVICE") public interface PaymentService { @GetMapping("/payment/{id}") @ResponseBody CommonRestult getPayment(@PathVariable("id") Integer id); @PostMapping("/payment") @ResponseBody public CommonRestult savePayment(@RequestBody Payment payment); }
@RestController public class OrderController { @Autowired private PaymentService paymentService; @PostMapping(value = "/consumer/openfeign/payment") public CommonRestult savePayment(@RequestBody Payment payment){ CommonRestult commonRestult = paymentService.savePayment(payment); return commonRestult; } @GetMapping("/consumer/openfeign/payment/{id}") public CommonRestult getPaymentById(@PathVariable("id") Integer id){ CommonRestult payment = paymentService.getPayment(id); return payment; } }
Feign自带负载均衡配置项
OpenFeign超时控制
1,在支付模块8001和支付模块8002的控制层添加方法(PaymentController.java)
@GetMapping("/payment/timeout") public String pyamentFeignTimeout(){ try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } return serverPort; }
2,在当前订单模块中,添加方法
PaymentService.java
@Component @FeignClient(value = "CLOUD-PAYMENT-SERVICE") public interface PaymentService { @GetMapping("/payment/{id}") @ResponseBody CommonRestult getPayment(@PathVariable("id") Integer id); @PostMapping("/payment") @ResponseBody CommonRestult savePayment(@RequestBody Payment payment); @GetMapping("/payment/timeout") String pyamentFeignTimeout(); }
OrderController.java
@RestController public class OrderController { @Autowired private PaymentService paymentService; @PostMapping(value = "/consumer/openfeign/payment") public CommonRestult savePayment(@RequestBody Payment payment){ CommonRestult commonRestult = paymentService.savePayment(payment); return commonRestult; } @GetMapping("/consumer/openfeign/payment/{id}") public CommonRestult getPaymentById(@PathVariable("id") Integer id){ CommonRestult payment = paymentService.getPayment(id); return payment; } @GetMapping("/consumer/payment/feign/timeout") public String pyamentFeignTimeout(){ return paymentService.pyamentFeignTimeout(); } }
执行http://localhost:8002/payment/timeout等待3秒钟OK,而执行http://localhost:84/consumer/payment/feign/timeout直接报错。
默认Feign客户端只等待1秒钟,但是服务端处理需要超过1秒钟,导致客户端等待超时,直接报错。为了避免这种情况,有时候需要设置Feign客户端的超时控制(yml文件中开启配置)。
OpenFeign默认支持Ribbon,所以通过ribbon属性值控制,YML文件里需要开启OpenFeign客户端超时控制。
server:
port: 84
spring:
application:
name: cloud-consumer-service
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
instance:
instance-id: order84
prefer-ip-address: true
ribbon:
#指的是建立连接后从服务器读取到可用资源所用的时间
ReadTimeout: 5000 #单位为毫秒
#指的是建立连接所用的时间,适用于网络正常的情况下,两端连接所用的时间
ConnectTimeOut: 5000 #单位为毫秒
OpenFeign进行日志打印
Feign提供了日志打印功能,可以通过配置来调整日志级别,从而了解Feign中Http请求的细节。也就是对Feign的接口调用情况进行监控和输出。
日志级别:
- NONE:默认的,不显示任何日志。
- BASIC:仅记录请求方法,URL,响应状态码及执行时间。
- HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息。
- FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文和元数据。
1,添加一个配置文件,配置日志bean
@Configuration
public class FeignConfig {
@Bean
public Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
或者在YML文件中配置
feign:
client:
config:
default:
loggerLevel: FULL
#针对某个级别设置输出级别(设置局部的优先级别是最高的)
Book-service:
loggerLevel: FULL
2,YML文件里需要开启日志的Feign客户端
server:
port: 84
spring:
application:
name: cloud-consumer-service
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
instance:
instance-id: order84
prefer-ip-address: true
ribbon:
#指的是建立连接后从服务器读取到可用资源所用的时间
ReadTimeout: 5000 #单位为毫秒
#指的是建立连接所用的时间,适用于网络正常的情况下,两端连接所用的时间
ConnectTimeOut: 5000 #单位为毫秒
logging:
level:
#feign日志以什么级别控制哪个接口
com.company.service.PaymentService: debug
后台日志打印
总结
Spring Cloud OpenFeign的核心工作原理:
- 通过@EnableFeignClients触发Spring应用程序对classpath中@FeignClient修饰类的扫描。
- 解析到@FeignClient修饰类后,Feign框架通过扩展Spring Bean Definition的注册逻辑,最终注册一个FeignClientFactoryBean进入Spring容器。
- Spring容器在初始化其他用到@FeignClient接口的类时,获得的是FeignClientFactoryBean产生的一个代理对象Proxy。
- 基于java原生的动态代理机制,针对Proxy的调用,都会被统一转发给Feign框架所定义的一个InvocationHandler,由该Handler完成后续的HTTP转换,发送,接收,翻译HTTP响应的工作。