一、OpenFeign 子產品間調用
現在的項目中已經使用 RestTemplate 實作子產品間的調用,為什麼還要使用 OpenFeign
因為 RestTemplate 是基于類調用,每次調用都需要 new 出 RestTemplate 類,耦合性很強。
1. OpenFeign 介紹
介紹 OpenFeign 之前先來介紹 Feign:
- feign 是一個聲明式的Web服務用戶端,讓編寫Web服務用戶端變得非常容易,隻需要建立一個接口并在接口上添加注釋即可
- Spring Cloud 對 Feign 進行了封裝,使其支援了 SpringMVC 标準注解和 HttpMessageConverters 。 Feign 可以與 Eureka 和 Ribbon 組合使用以支援負載均衡。
使用的目的為了用接口的方式讓子產品之間的調用更加靈活,而不是 new 出一個個的類
Feign 內建了 Ribbon ,與 Ribbon 不同的是,通過 Feign 隻需要定義服務綁定接口且以聲明式的方法,優雅而簡單的實作了服務調用。
OpenFeign 是 Spring Cloud 在 Feign 的基礎上繼續進行封裝,支援了 SpringMVC 的注釋,如@RequesMapping等等。
OpenFeign 的 @FeignClient 可以解析 SpringMVC 的 @RequesMapping 注釋下的接口,并通過動态代理的方式産生實作類,實作類中做負載均衡并調用其他服務。
2. 使用方式
服務提供方(生産者)和服務調用方(消費者)在注冊中心注冊後,服務調用方由原來的 Ribbon + RestTemplate 的方式改為使用 OpenFeign 方式調用,使用方式為:
微服務調用接口 + @FeignClient
二、目前項目結構介紹
目前項目中包含四個子產品:
- 公有API:cloud-api-commons
- 服務提供者: cloud-provider-payment8001/8002 (端口号8001 和 8002 )
- 服務消費者: cloud-consumer-order80 (端口号80 )
- 注冊中心 Eureka: cloud-eureka-server7001(端口号7001 )
前文連結: https://blog.csdn.net/weixin_42547014/article/details/120334570
項目源碼:https://gitee.com/zhangchouchou/spring-cloud-demo
三、項目中添加 Feign
1. 建立子產品
建立一個子產品,命名為
cloud-consumer-feign-order80
,添加方式依舊為 Maven
2. POM 引入依賴
注:OpenFeign 也是自帶 Ribbon
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>SpringCloudDemo</artifactId>
<groupId>org.zjh.springclouddemo</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-consumer-feign-order80</artifactId>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<!--OpenFeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--Eureka-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.zjh.springclouddemo</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</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>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!--小辣椒-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
3. 添加 YML
server:
port: 80
spring:
application:
name: cloud-consumer-feign-order80
eureka:
client:
# 是否注冊到eureka
register-with-eureka: true
# 是否發現相關服務
fetch-registry: true
# 指定路徑
service-url:
defaultZone: http://localhost:7001/eureka
4. 建立啟動類
在啟動類上添加 @EnableFeignClients 注解,該注解隻在服務調用方添加即可。
@SpringBootApplication
// Eureka用戶端
@EnableEurekaClient
//服務調用方開啟Feign
@EnableFeignClients
public class FeignConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(FeignConsumerApplication.class,args);
}
}
5. 添加調用接口
添加接口
PaymentFeignService
目錄結構
//作為元件被發現
@Component
//指定遠端調用的微服務的名稱
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService {
@GetMapping("/user/get/{id}")
public CommonResult<User> getByID(@PathVariable("id")Integer id);
}
6. 實作 Controller
@RestController
@Slf4j
public class FeignController {
@Autowired
private PaymentFeignService paymentFeignService; //調用遠端的微服務接口
@GetMapping("/consumer/user/get/{id}")
public CommonResult<User> getByID(@PathVariable("id")Integer id){
return paymentFeignService.getByID(id);
}
}
至此便實作了子產品間接口的遠端調用。
7. 驗證
啟動注冊中心 -> 啟動8001端口子產品 -> 啟動建立的子產品
重新整理注冊中心看建立的服務是否注冊上
注冊成功後,通路新啟動的80端口進行資料擷取
資料成功擷取,說明配置成功。
四、請求逾時處理
子產品之間的調用很有可能出現請求逾時的情況,給 8001 端口的生産者添加一個新的請求,該請求不做任何查詢,在方法中等待幾秒,如下所示:
@GetMapping("/user/timeout")
public String feignTimeout(){
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
//傳回端口号
return port;
}
再在剛剛添加的 Feign 方式的消費者的接口中添加方法
最後在Controller中添加請求
@GetMapping("/consumer/user/timeout")
public String feignTimeout(){
return paymentFeignService.feignTimeout();
}
重新啟動服務并通路路徑,因為 OpenFeign 預設等待1秒,是以會出現請求逾時的錯誤
處理方式很簡單,設定逾時時間。
在 YML 中開啟 OpenFeign 用戶端逾時控制。
ribbon:
ReadTimeout: 5000 #讀取的逾時時間
ConnectTimeout: 5000 #連結的逾時時間
MaxAutoRetries: 1 #同一台執行個體最大重試次數,不包括首次調用
MaxAutoRetriesNextServer: 1 #重試負載均衡其他的執行個體最大重試次數,不包括首次調用
OkToRetryOnAllOperations: false #是否所有操作都重試
當然,這種方法治标不治本,真正的解決方式是使用斷路器(Hystrix),會在後文講解。
五、日志列印
留意一下控制台可以發現一些列印出來的請求資訊, Feign 支援列印接口調用情況,友善進行接口監控。
日志級别:
- NONE:預設的,不顯示任何日志
- BASIC:僅記錄請求方法、URL、響應狀态碼及執行時間。
- HEADERS: 除了BASIC中定義的資訊外,還有請求和響應頭資訊
- FULL: 除了HEADERS中定義的資訊外,還有請求及響應的正文及中繼資料
配置日志 Bean
@SpringBootConfiguration
public class FeignConfig {
@Bean
public Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
YML 中開啟日志的 Feign 用戶端
logging:
level:
org.zjh.springcloud.service.PaymentFeignService: debug
重新開機後再次查詢資訊,可以看到列印的日志資訊
2021-09-17 17:06:53.825 INFO 808 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty : Flipping property: CLOUD-PAYMENT-SERVICE.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2021-09-17 17:06:55.223 DEBUG 808 --- [p-nio-80-exec-1] o.z.s.service.PaymentFeignService : [PaymentFeignService#getByID] <--- HTTP/1.1 200 (2906ms)
2021-09-17 17:06:55.223 DEBUG 808 --- [p-nio-80-exec-1] o.z.s.service.PaymentFeignService : [PaymentFeignService#getByID] connection: keep-alive
2021-09-17 17:06:55.223 DEBUG 808 --- [p-nio-80-exec-1] o.z.s.service.PaymentFeignService : [PaymentFeignService#getByID] content-type: application/json
2021-09-17 17:06:55.223 DEBUG 808 --- [p-nio-80-exec-1] o.z.s.service.PaymentFeignService : [PaymentFeignService#getByID] date: Fri, 17 Sep 2021 09:06:55 GMT
2021-09-17 17:06:55.224 DEBUG 808 --- [p-nio-80-exec-1] o.z.s.service.PaymentFeignService : [PaymentFeignService#getByID] keep-alive: timeout=60
2021-09-17 17:06:55.224 DEBUG 808 --- [p-nio-80-exec-1] o.z.s.service.PaymentFeignService : [PaymentFeignService#getByID] transfer-encoding: chunked
2021-09-17 17:06:55.224 DEBUG 808 --- [p-nio-80-exec-1] o.z.s.service.PaymentFeignService : [PaymentFeignService#getByID]
2021-09-17 17:06:55.225 DEBUG 808 --- [p-nio-80-exec-1] o.z.s.service.PaymentFeignService : [PaymentFeignService#getByID] {"code":200,"message":"查詢成功","data":{"id":6,"name":"吳","phone":"19804563454","sex":"女"}}
2021-09-17 17:06:55.225 DEBUG 808 --- [p-nio-80-exec-1] o.z.s.service.PaymentFeignService : [PaymentFeignService#getByID] <--- END HTTP (100-byte body)