作为Spring Cloud的子项目之一,Spring Cloud OpenFeign以将OpenFeign集成到Spring Boot应用中的方式,为微服务架构下服务之间的调用提供了解决方案。首先,利用了OpenFeign的声明式方式定义Web服务客户端;其次还更进一步,通过集成Ribbon或Eureka实现负载均衡的HTTP客户端。
Feign,假装、伪装。OpenFeign可以使消费者将提供者提供的服务名伪装为接口进行消费,消费者只需使用“Service接口 + 注解”的方式即可直接调用Service接口方法,而无需再使用RestTemplate了。
Ribbon与OpenFeign
说到OpenFeign,不得不提的就是Ribbon。Ribbon是Netflix公司的一个开源的负载均衡项目,是一个客户端负载均衡器,运行在消费者端。
OpenFeign也是运行在消费者端的,使用Ribbon进行负载均衡,所以OpenFeign直接内置了Ribbon。即在导入OpenFeign依赖后,无需再专门导入Ribbon依赖了
@FeignClient 标注用于声明Feign客户端可访问的Web服务。
@EnableFeignClients 标注用于修饰Spring Boot应用的入口类,以通知Spring Boot启动应用时,扫描应用中声明的Feign客户端可访问的Web服务。
准备
eureka注册中心:
cloud-eureka-server-7001
服务提供者:
cloud-eureka-client-provider-8001
cloud-eureka-client-provider-8002
上述服务配置:https://blog.youkuaiyun.com/m0_45097637/article/details/112574536
配置OpenFeign服务调用
cloud-consumer-feign-order-80
端口: 80
服务名: consumer-feign-order
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--继承父工程-->
<parent>
<groupId>com.mxd</groupId>
<artifactId>cloud-parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.mxd</groupId>
<artifactId>cloud-consumer-feign-order-80</artifactId>
<version>1.0-SNAPSHOT</version>
<name>cloud-consumer-feign-order-80</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>2020.0.0</spring-cloud.version>
</properties>
<dependencies>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
controller
package com.mxd.cloudconsumerfeignorder80.controller;
import com.mxd.cloudconsumerfeignorder80.service.OrderFeignService;
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
public class OrderFeignController {
// 注意,这里使用的是Feign接口,而不是业务接口
@Resource
private OrderFeignService orderFeignService;
@GetMapping("/consumer/provider/get/{id}")
public String provider(@PathVariable("id") int id) {
return orderFeignService.provider(id);
}
}
service
package com.mxd.cloudconsumerfeignorder80.service;
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 = "EUREKA-PROVIDER-SERVICE")
public interface OrderFeignService {
// 这里的接口名称,及方法名可以随意定义,但方法参数与方法上的注解必须要与其提供者端对应方法的相同
@GetMapping("/provider/get/{id}")
public String provider(@PathVariable("id") int id);
}
启动类
package com.mxd.cloudconsumerfeignorder80;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients // 开启Feign客尸骗,也可以指定service所在的包
public class OrderFeignMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderFeignMain80.class, args);
}
}
application.yml
server:
port: 80
spring:
application:
name: consumer-feign-order
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/ #注册地址
启动
依次启动一下服务
cloud-eureka-server-7001
cloud-eureka-client-provider-8001
cloud-eureka-client-provider-8002
cloud-consumer-feign-order-80
测试
eureka注册中心 http://localhost:7001/
不断请求 http://localhost/consumer/provider/get/5 ,观察变化
Open Feign超时控制
修改yml
server:
port: 80
spring:
application:
name: consumer-feign-order
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/ #注册地址
#设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
#指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
ReadTimeout: 5000
#指的是建立连接后从服务器读取可用资源所用的时间
ConnectTimeout: 5000
修改cloud-eureka-client-provider-8001和cloud-eureka-client-provider-8002
controller添加
@GetMapping(value = "/payment/feign/timeout")
public String paymentFeignTimeOut(){
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
return serverPort;
}
修改controller
package com.mxd.cloudconsumerfeignorder80.controller;
import com.mxd.cloudconsumerfeignorder80.service.OrderFeignService;
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
public class OrderFeignController {
@Resource
private OrderFeignService orderFeignService;
@GetMapping("/consumer/provider/get/{id}")
public String provider(@PathVariable("id") int id) {
return orderFeignService.provider(id);
}
// 添加的
@GetMapping(value = "/consumer/payment/feign/timeout")
public String paymentFeignTimeOut() {
return orderFeignService.paymentFeignTimeOut();
}
}
修改 serivice
package com.mxd.cloudconsumerfeignorder80.service;
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 = "EUREKA-PROVIDER-SERVICE")
public interface OrderFeignService {
@GetMapping("/provider/get/{id}")
public String provider(@PathVariable("id") int id);
// 添加的
@GetMapping(value = "/payment/feign/timeout")
public String paymentFeignTimeOut();
}
测试
依次重启
cloud-eureka-client-provider-8001
cloud-eureka-client-provider-8002
cloud-consumer-feign-order-80
多次请求,可以发现,每次请求都要等待3s才会响应,如果yml没有设置feign客户端超时时间,则会直接报错
http://localhost/consumer/payment/feign/timeout