SpringCloud Feign
Feign介紹
Feign是一個聲明式的Web Service客戶端,它使得編寫Web Serivce客戶端變得更加簡單。我們只需要使用Feign來創建一個接口並用註解來配置它既可完成。
它具備可插拔的註解支持,包括Feign註解和JAX-RS註解。Feign也支持可插拔的編碼器和×××。Spring Cloud爲Feign增加了對Spring MVC註解的支持,
還整合了Ribbon和Eureka來提供均衡負載的HTTP客戶端實現。
Feign原理簡述
- 啓動時,程序會進行包掃描,掃描所有包下所有@FeignClient註解的類,並將這些類注入到spring的IOC容器中。
- 當定義的Feign中的接口被調用時,通過JDK的動態代理來生成RequestTemplate。
- RequestTemplate中包含請求的所有信息,如請求參數,請求URL等。
- RequestTemplate聲場Request,然後將Request交給client處理,這個client默認是JDK的HTTPUrlConnection,也可以是OKhttp、Apache的HTTPClient等。
最後client封裝成LoadBaLanceClient,結合ribbon負載均衡地發起調用。
Feign遠程調用的基本流程
1.Feign核心:將以java註解的方式定義的遠程調用API接口,最終轉換成HTTP的請求形式,然後將HTTP的請求的響應結果,解碼成JAVA Bean,放回給調用者。
2.Feign通過處理註解,將請求模板化,當實際調用的時候,傳入參數,
根據參數再應用到請求上,進而轉化成真正的 Request 請求。
Feign遠程調用的重要組件
在微服務啓動時,Feign會進行包掃描,對加@FeignClient註解的接口,
按照註解的規則,創建遠程接口的本地JDK Proxy代理實例。然後,
將這些本地Proxy代理實例,注入到Spring IOC容器中。當遠程接口的方法被調用,由Proxy代理實例去完成真正的遠程訪問,並且返回結果。
關鍵註解
- @EnableFeignClients
- @FeignClient
@EnableFeignClients
該註解作用於啓動類
@FeignClient
- FeignClient註解被@Target(ElementType.TYPE)修飾,表示FeignClient註解的作用目標在接口上
- @FeignClient標籤的常用屬性如下
- name:指定FeignClient的名稱,如果項目使用了Ribbon,name屬性會作爲微服務的名稱,用於服務發現
- url: url一般用於調試,可以手動指定@FeignClient調用的地址
- decode404:當發生http 404錯誤時,如果該字段位true,會調用decoder進行解碼,否則拋出FeignException
- configuration: Feign配置類,可以自定義Feign的Encoder、Decoder、LogLevel、Contract
- fallback: 定義容錯的處理類,當調用遠程接口失敗或超時時,會調用對應接口的容錯邏輯,fallback指定的類必須實現@FeignClient標記的接口
- fallbackFactory: 工廠類,用於生成fallback類示例,通過這個屬性我們可以實現每個接口通用的容錯邏輯,減少重複的代碼
- path: 定義當前FeignClient的統一前綴
環境準備
開發環境
- JDK:1.8
- SpringBoot:2.1.16.RELEASE
- SpringCloud:Finchley
項目結構
- halo-cloud-parent 父工程
- halo-cloud-provider 服務提供者
- halo-cloud-feign 服務消費者
- halo-cloud-server 註冊中心
Feign使用
halo-cloud-parent
- 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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.16.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.cloud</groupId>
<artifactId>parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>parent</name>
<description>父工程</description>
<properties>
<java.version>1.8</java.version>
</properties>
<!--打包方式-->
<packaging>pom</packaging>
<modules>
<module>halo-cloud-server</module>
<module>halo-cloud-feign</module>
<module>halo-cloud-provider</module>
</modules>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--web依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<!--引入springcloud依賴的-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
halo-cloud-server
- pom.xml
<!--eureka server 依賴座標-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
- application.yml
server: # 服務端口
port: 9090
spring:
application: # 應用名字,eureka 會根據它作爲服務id
name: halo-cloud-server
# eureka配置
eureka:
instance:
hostname: localhost
client:
service-url: # eureka server 的地址, 咱們單實例模式就寫自己好了
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka
register-with-eureka: false # 不向eureka server 註冊自己
fetch-registry: false # 不向eureka server 獲取服務列表
- ServerApplication.java
@EnableEurekaServer
@SpringBootApplication
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
}
halo-cloud-feign
- pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- application.yml
server:
port: 8080 #端口
spring:
application:
name: halo-cloud-fiegn #服務名稱
eureka:
client:
service-url: # eureka server 的地址, 咱們單實例模式就寫自己好了
defaultZone: http://localhost:9090/eureka
fetch-registry: true #表示是否將自己註冊到Eureka Server,默認是true。
register-with-eureka: true #表示是否從Eureka Server獲取註冊信息,默認爲true。
- FeignApplication.java
@EnableEurekaClient
@EnableFeignClients
@SpringBootApplication
public class FeignApplication {
public static void main(String[] args) {
SpringApplication.run(FeignApplication.class, args);
}
}
halo-cloud-provider
- pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- application.yml
server: # 服務端口
port: 7070
spring:
application: # 應用名字,eureka 會根據它作爲服務id
name: halo-cloud-provider
# eureka配置
eureka:
instance:
hostname: spring-cloud-provider
client:
service-url: # eureka server 的地址, 咱們單實例模式就寫自己好了
defaultZone: http://localhost:9090/eureka
register-with-eureka: true # 不向eureka server 註冊自己
fetch-registry: true # 不向eureka server 獲取服務列表
- ProviderApplication.java
@EnableEurekaClient
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
- ProviderController.java
@RestController
@RequestMapping("/provider")
public class ProviderController {
@Value("${server.port}")
private Integer port;
@GetMapping("/helloProvider")
public String helloProvider(String name){
return name+"正在調用provider端口:"+String.valueOf(port);
}
}
啓動測試
-
註冊中心
-
服務者測試
-
消費者調用服務測試