服務消費
當我們實現了服務註冊的時候,也同步獲取到了註冊成功的服務資訊,就可以直接呼叫服務,我們今天來實踐下。
一、準備工作
1.1 建立 Eureka- Server
- 新建 Eureka Server 模組,新增 Eureka Server 依賴
<properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR4</spring-cloud.version> </properties> <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> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
- 在配置檔案
resources/application. yml
中新增EurekaServer
相關配置
# 指定執行埠 server: port: 9000 spring: application: # 指定服務名稱 name: eureka-server eureka: instance: # 指定主機地址 hostname: localhost client: # 指定是否從註冊中心獲取服務(註冊中心不需要開啟) fetch-registry: false # 指定是否將自身註冊到註冊中心(註冊中心不需要開啟) register-with-eureka: false
- 在啟動類上新增
@EnableEurekaServer
註解來啟用Euerka
註冊中心功能
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
- 啟動專案,訪問 http://localhost:9000 ,可以看到
Eureka
註冊中心介面:
1.2 建立 PriceService 模組
- 新增依賴
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR4</spring-cloud.version>
</properties>
<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>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
- 在配置檔案
resources/application. yml
新增相關配置
spring:
application:
# 指定服務名稱
name: price-service
eureka:
client:
# 註冊到 Eureka 的註冊中心
register-with-eureka: true
# 獲取註冊例項列表
fetch-registry: true
service-url:
# 指定註冊中心
defaultZone: http://localhost:9000/eureka
- 在啟動類上新增 @EnableDiscoveryClient 註解
@EnableDiscoveryClient
@SpringBootApplication
public class PriceServiceApplication {
public static void main(String[] args) {
SpringApplication.run(PriceServiceApplication.class, args);
}
}
- 建立 PriceController,實現根據商品Id 獲取價格功能
@RestController
@RequestMapping("/price")
public class PriceController {
@GetMapping("/getPrice/{productId}")
public BigDecimal getPrice(@PathVariable("productId") String productId) {
System.out.println("productId=" + productId);
return new BigDecimal("100");
}
}
- 啟動兩個 PriceService 服務
1.3 建立 OrderService 模組
- 新增依賴
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR4</spring-cloud.version>
</properties>
<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>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
- 在配置檔案
resources/application. yml
新增相關配置
server:
# 指定執行埠
port: 8005
spring:
application:
# 指定服務名稱
name: order-service
eureka:
client:
# 註冊到 Eureka 的註冊中心
register-with-eureka: true
# 獲取註冊例項列表
fetch-registry: true
service-url:
# 指定註冊中心
defaultZone: http://localhost:9000/eureka
- 在啟動類上新增 @EnableDiscoveryClient 註解
@EnableDiscoveryClient
@SpringBootApplication
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
- 新建 OrderController 用於訪問 PriceService 獲取價格
@RestController
@RequestMapping("/order")
public class OrderController {
private final RestTemplate restTemplate;
public OrderController(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@GetMapping("/getPrice")
public BigDecimal getPrice(String productId) {
// TODO 方法待實現
return null;
}
}
二、負載均衡
當一個服務存在多個例項的,我們就需要使用能夠滿足負載均衡的 HTTP 元件。
2.1 Loadbalancer
2.1.1 Loadbalancer 介紹
LoadBalancerClient 是 SpringCloud 提供的負載均衡器客戶端,它先從提供的服務中獲取某一個例項(預設策略為輪詢),通過 choose() 方法獲取到節點中的一個服務,拿到服務的資訊之後取出服務 IP 資訊,就可以得到完成的想要訪問的 IP地址和介面,最後通過 RestTempate 訪問服務。
2.1.2 Loadbalancer 使用
- 新建 HttpConfig ,啟用 @LoadBalanced 實現負載均衡功能
@Configuration
public class HttpConfig {
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
- 完善 OrderController.getPrice() 方法
@GetMapping("/getPrice")
public BigDecimal getPrice(String productId) {
final BigDecimal price = restTemplate.getForObject("http://PRICE-SERVICE/price/getPrice/" + productId, BigDecimal.class);
return price;
}
- 啟動 OrderService 服務,訪問
http://localhost:8005/order/getPrice?productId=123
,返回:
100
說明呼叫成功了,我們再訪問二次請求,然後檢視 PriceService 控制檯輸出:
檢視 PriceService01控制檯:
productId=123
productId=123
檢視 PriceService02 控制檯:
productId=123
可以看到使用了輪詢機制。
2.2 Ribbon
Spring Cloud Netflix Ribbon 是 Spring Cloud Netflix 子專案的核心元件之一,主要給服務間呼叫及 API 閘道器轉發提供負載均衡的功能。
2.2.1 Ribbon的常用配置
全域性配置:
ribbon:
ConnectTimeout: 1000 #服務請求連線超時時間(毫秒)
ReadTimeout: 3000 #服務請求處理超時時間(毫秒)
OkToRetryOnAllOperations: true #對超時請求啟用重試機制
MaxAutoRetriesNextServer: 1 #切換重試例項的最大個數
MaxAutoRetries: 1 # 切換例項後重試最大次數
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #修改負載均衡演算法
指定服務進行配置:
user-service:
ribbon:
ConnectTimeout: 1000 #服務請求連線超時時間(毫秒)
ReadTimeout: 3000 #服務請求處理超時時間(毫秒)
OkToRetryOnAllOperations: true #對超時請求啟用重試機制
MaxAutoRetriesNextServer: 1 #切換重試例項的最大個數
MaxAutoRetries: 1 # 切換例項後重試最大次數
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #修改負載均衡演算法
可以看出與全域性配置的區別就是 ribbon 節點掛在服務名稱下面。
2.2.2 Ribbon的負載均衡策略
ribbon可以選擇以下幾種負載均衡策略:
* com.netflix.loadbalancer.RandomRule:從提供服務的例項中以隨機的方式;
* com.netflix.loadbalancer.RoundRobinRule:以線性輪詢的方式,就是維護一個計數器,從提供服務的例項中按順序選取,第一次選第一個,第二次選第二個,以此類推,到最後一個以後再從頭來過;
* com.netflix.loadbalancer.RetryRule:在RoundRobinRule的基礎上新增重試機制,即在指定的重試時間內,反覆使用線性輪詢策略來選擇可用例項;
* com.netflix.loadbalancer.WeightedResponseTimeRule:對RoundRobinRule的擴充套件,響應速度越快的例項選擇權重越大,越容易被選擇;
* com.netflix.loadbalancer.BestAvailableRule:選擇併發較小的例項;
* com.netflix.loadbalancer.AvailabilityFilteringRule:先過濾掉故障例項,再選擇併發較小的例項;
* com.netflix.loadbalancer.ZoneAwareLoadBalancer:採用雙重過濾,同時過濾不是同一區域的例項和故障例項,選擇併發較小的例項。
三、Feign
3.1 Feign 介紹
Feign 是一個宣告式的Web Service客戶端,它使得編寫 Web Serivce 客戶端變得更加簡單。我們只需要使用Feign 來建立一個介面並用註解來配置它既可完成。它具備可插拔的註解支援,包括 Feign註解和 JAX-RS 註解。Feign 也支援可插拔的編碼器和解碼器。Spring Cloud 為 Feign 增加了對 Spring MVC 註解的支援,還整合了Ribbon 和 Eureka 來提供負載均衡的HTTP客戶端實現。
3.2 FeignClient註解屬性
屬性名 | 預設值 | 作用 | 備註 |
---|---|---|---|
value | 空字串 | 呼叫服務名稱,和name屬性相同 | |
serviceId | 空字串 | 服務id,作用和name屬性相同 | 已過期 |
name | 空字串 | 呼叫服務名稱,和value屬性相同 | |
url | 空字串 | 全路徑地址或hostname,http或https可選 | |
decode404 | false | 配置響應狀態碼為404時是否應該丟擲FeignExceptions | |
configuration | {} | 自定義當前feign client的一些配置 | 參考FeignClientsConfiguration |
fallback | void.class | 熔斷機制,呼叫失敗時,走的一些回退方法,可以用來丟擲異常或給出預設返回資料。 | 底層依賴hystrix,啟動類要加上@EnableHystrix |
path | 空字串 | 自動給所有方法的requestMapping前加上字首,類似與controller類上的requestMapping | |
primary | true |
3.3 Feign 簡單使用
- 在 OrderService 模組中新增 Feign 依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 啟用 Feign 註解
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
- 建立 PriceService 介面
@FeignClient(value = "PRICE-SERVICE", path = "/price")
public interface PriceService {
@RequestMapping(value = "/getPrice/{productId}", method = RequestMethod.GET)
BigDecimal getPrice(@PathVariable("productId")String productId);
}
可以看到相當於是一個和 PriceService 訪問的契約。
- 修改 OrderController 呼叫邏輯
@RestController
@RequestMapping("/order")
public class OrderController {
private final PriceService priceService;
public OrderController(PriceService priceService) {
this.priceService = priceService;
}
@GetMapping("/getPrice")
public BigDecimal getPrice(String productId) {
final BigDecimal price = priceService.getPrice(productId);
return price;
}
}
5)執行 OrderService 服務,訪問 http://localhost:8005/order/getPrice?productId=123
,返回:
100
說明呼叫成功。