Feign是一個聲明似的web服務客戶端,它使得編寫web服務客戶端變得更加容易。使用Fegin建立一個接口並對它進行註解。它具備可插拔的註解支持包括Feign註解與JAX-RS註解,Feign還支持可插拔的編碼器與解碼器,Spring Cloud 增長了對 Spring MVC的註解,Spring Web 默認使用了HttpMessageConverters, Spring Cloud 集成 Ribbon 和 Eureka 提供的負載均衡的HTTP客戶端 Feign。html
官方解釋: Feign is a Java to HTTP client binder inspired by Retrofit, JAXRS-2.0, and WebSocket. Feign's first goal was reducing the complexity of binding Denominator uniformly to HTTP APIs regardless of ReSTfulness.java
以下圖所示, service-b 聲明一個接口, 去調用路徑爲 /user/get 的服務 service-a 的服務git
以下圖, 在被調用者中聲明接口, 去調用本身, 這種方法遵循面向接口編程, 並且使用起來, 就相似dubbo同樣, @Autowire直接注入就可使用了.github
以上可能看得讀者一頭霧水, 如下具體的代碼流程, 能夠方便更加具體的瞭解web
如下步驟爲手把手教學, 請明白個人良苦用心spring
首先建立一個父項目apache
把其餘都刪除, 剩下pom文件編程
如下爲父項目的依賴api
<?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> <groupId>com.fegin</groupId> <artifactId>test</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>pom</packaging> <!--springboot version 2.1.4--> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.SR1</spring-cloud.version> </properties> <!--springcloud version Greenwish.SR1--> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project>
建立eureka項目瀏覽器
項目結構如圖
添加eureka的依賴
<?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>test</artifactId> <groupId>com.fegin</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>eureka</artifactId> <!--eureka服務端配置--> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies> </project>
添加application.yml
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
# 是否把本身做爲服務註冊到其餘服務註冊中心
registerWithEureka: false
# 是否從其餘的服務中心同步服務列表
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
server:
# 關閉保護機制,默認true
enable-self-preservation: false
# 剔除失效服務間隔,默認60000
eviction-interval-timer-in-ms: 3000
添加啓動類代碼EurekaApplication
/** * @author c-can-z */ @EnableEurekaServer @SpringBootApplication public class EurekaApplication { public static void main(String[] args) { SpringApplication.run(EurekaApplication.class,args); } }
瀏覽器輸入 http://localhost:8761/
建立一個serviceb
添加一下serviceb必須的依賴
<?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>test</artifactId> <groupId>com.fegin</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>serviceb</artifactId> <dependencies> <!--springboot web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--springboot 測試--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--eureka客戶端配置--> <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-netflix-ribbon</artifactId> </dependency> <!--微服務調用--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!--zipkin客戶端配置, 已經包含sleuth--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin</artifactId> </dependency> </dependencies> </project>
把 application.properties 修改成 application.yml, 本服務名稱爲 service-b 端口爲 9992
server:
port: 9992
spring:
application:
name: service-b
zipkin:
base-url: http://localhost:9411
sleuth:
sampler:
probability: 1
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
registry-fetch-interval-seconds: 5 #eureka client刷新本地緩存時間,默認30
instance:
prefer-ip-address: true
#Eureka客戶端向服務端發送心跳的時間間隔,單位爲秒(客戶端告訴服務端本身會按照該規則),默認30
lease-renewal-interval-in-seconds: 5
#Eureka服務端在收到最後一次心跳以後等待的時間上限,單位爲秒,超過則剔除(客戶端告訴服務端按照此規則等待本身),默認90
lease-expiration-duration-in-seconds: 7
feign:
client:
config:
default:
connectTimeout: 7000
readTimeout: 7000
service-b:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
logging:
level:
root: info
導入啓動類文件
@SpringBootApplication @EnableFeignClients public class SeriveBApplication { public static void main(String[] args) { SpringApplication.run(SeriveBApplication.class,args); } }
建立Feign客戶端
//在建立該步驟的時候, 須要關注一下步驟1的說明 //@FeignClient(name = "service-a")註解來綁定該接口對應servic-a服務 @FeignClient(name = "service-a") public interface UserFeginClient { //service-a服務對應資源路徑.必須加上@RequestParam, 不然會報錯,返回參數也必須對應上 @RequestMapping("user/get") String get(@RequestParam("id")Long id); }
在controller中直接進行調用
@RestController @RequestMapping("/product") public class ProductController { @Autowired private UserFeginClient userFeginClient; @RequestMapping("/get") public String get(Long id){ return "產品服務抽獎: "+userFeginClient.get(id); } }
<?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>test</artifactId> <groupId>com.fegin</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>servicec-api</artifactId> <dependencies> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--微服務調用--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies> </project>
實體類product
public class Product implements Serializable { private Long id; private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; }
}
feign客戶端
/** * 服務名稱 * @author c-can-z */ @FeignClient(name="service-c") public interface ProductFeignApi { //動態代理須要的地址, 可是咱們實際操做不到 @RequestMapping("/servicec/get") Product get(@RequestParam("id") Long id); }
項目結構
service項目依賴
<?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>test</artifactId> <groupId>com.fegin</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>servicec</artifactId> <dependencies> <!--springboot web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--eureka客戶端配置--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!--zipkin客戶端配置, 已經包含sleuth--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin</artifactId> </dependency> <!--springboot 測試--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--導入api--> <dependency> <groupId>com.fegin</groupId> <artifactId>servicec-api</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> </dependencies> </project>
servicec的 application.yml
server:
port: 9993
spring:
application:
name: service-c
zipkin:
base-url: http://localhost:9411
sleuth:
sampler:
probability: 1
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
registry-fetch-interval-seconds: 5 #eureka client刷新本地緩存時間,默認30
instance:
prefer-ip-address: true
#Eureka客戶端向服務端發送心跳的時間間隔,單位爲秒(客戶端告訴服務端本身會按照該規則),默認30
lease-renewal-interval-in-seconds: 5
#Eureka服務端在收到最後一次心跳以後等待的時間上限,單位爲秒,超過則剔除(客戶端告訴服務端按照此規則等待本身),默認90
lease-expiration-duration-in-seconds: 7
feign:
client:
config:
default:
connectTimeout: 7000
readTimeout: 7000
logging:
level:
root: info
實現Feign客戶端接口, 也是該文章的核心代碼
採用實現的方式
/** * 遠程調用接口的實現類 * @author c-can-z */ @RestController public class ProductFeignClient implements ProductFeignApi { @Override public Product get(Long id) { Product product = new Product(); product.setId(id); product.setName("我是服務C"); return product; } }
servicec的啓動類
@SpringBootApplication public class ProductServerApplication { public static void main(String[] args) { SpringApplication.run(ProductServerApplication.class, args); } }
項目結構
注意啓動類的位置, servicecapi的路徑必須被啓動類掃描到
serviced的依賴,
注意, 必須引入servicec的api, 有人會說有代碼侵入的問題, 可是對比案例1, 若是多個項目調用, 要建立多個Feign客戶端, 孰是孰非, 還得看項目的具體需求
<?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>test</artifactId> <groupId>com.fegin</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>serviced</artifactId> <dependencies> <!--springboot web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--springboot 測試--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--eureka客戶端配置--> <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> <!--zipkin客戶端配置, 已經包含sleuth--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin</artifactId> </dependency> <dependency> <groupId>com.fegin</groupId> <artifactId>servicec-api</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> </dependencies> </project>
serviced的 application.yml
server:
port: 9994
spring:
application:
name: service-d
zipkin:
base-url: http://localhost:9411
sleuth:
sampler:
probability: 1
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
registry-fetch-interval-seconds: 5 #eureka client刷新本地緩存時間,默認30
instance:
prefer-ip-address: true
#Eureka客戶端向服務端發送心跳的時間間隔,單位爲秒(客戶端告訴服務端本身會按照該規則),默認30
lease-renewal-interval-in-seconds: 5
#Eureka服務端在收到最後一次心跳以後等待的時間上限,單位爲秒,超過則剔除(客戶端告訴服務端按照此規則等待本身),默認90
lease-expiration-duration-in-seconds: 7
feign:
client:
config:
default:
connectTimeout: 7000
readTimeout: 7000
logging:
level:
root: info
serviced的控制類
/** * @author c-can-z */ @RestController @RequestMapping("/order") public class OrderController { @Autowired private ProductFeignApi productFeignApi; @RequestMapping("/get") public String get(Long id){ Product product = productFeignApi.get(id); return "訂單爲: 貨品:" + product.getName() + ", 貨品id:"+product.getId(); } }
serviced的啓動類
/** * @author c-can-z */ @SpringBootApplication @EnableFeignClients public class ServiceDApplication { public static void main(String[] args) { SpringApplication.run(ServiceDApplication.class,args); } }
http://localhost:9994/order/get?id=5
到了這裏, 不知道你是否理解 直接在調用者聲明Feign客戶端 或者 在被調用者接口Api中聲明Feign客戶端
每個服務調用其餘服務, 就須要建立一個客戶端, 從代碼方面來講, 相對比較麻煩
從調用者來看, 使用起來就跟使用淘寶的dubbo同樣方便, 可是每個服務調用其餘服務, 就須要引入其餘服務的api依賴, 從項目之間的互相依賴來看, 相對來講, 也會比較麻煩.