服務短路
近義詞
- 服務容錯(Fault tolerance):強調容忍錯誤,不至于整體故障
- 服務降級(downgrade):強調服務非強依賴,不影響主要流程
以web工程為例
建立DemoRestController
package com.segmentfault.spring.cloud.lesson8.web.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Random;
import java.util.concurrent.TimeoutException;
/**
* Demo RestController
*
*/
@RestController
public class DemoRestController {
private final static Random random = new Random();
/**
* 當方法執行時間超過 100 ms 時,觸發異常
*
* @return
*/
@GetMapping("")
public String index() throws Exception {
long executeTime = random.nextInt(200);
if (executeTime > 100) { // 執行時間超過了 100 ms
throw new TimeoutException("Execution is timeout!");
}
return "Hello,World";
}
}
異常處理
通過@RestControllerAdvice實作
package com.segmentfault.spring.cloud.lesson8.web.controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.concurrent.TimeoutException;
/**
* {@link DemoRestController} 類似于AOP 攔截
*
*/
@RestControllerAdvice(assignableTypes = DemoRestController.class)
public class DemoRestControllerAdvice {
@ExceptionHandler(TimeoutException.class)
public Object faultToleranceTimeout(Throwable throwable) {
return throwable.getMessage();
}
}
Spring Cloud Netflix Hystrix
增加Maven依賴
<dependencyManagement>
<dependencies>
<!-- Spring Boot 依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.5.8.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Spring Cloud 依賴 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- 其他依賴省略 -->
<!-- 依賴 Spring Cloud Netflix Hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
</dependencies>
使用 @EnableHystrix
實作服務提供方短路
@EnableHystrix
修改應用user-service-provider的引導類:
package com.segumentfault.spring.cloud.lesson8.user.service;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
/**
* 引導類
*
*/
@SpringBootApplication
@EnableHystrix
public class UserServiceProviderApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceProviderApplication.class, args);
}
}
通過@HystrixCommand實作
增加getUsers()方法到UserServiceProviderController
/**
* 擷取所有使用者清單
*
* @return
*/
@HystrixCommand(
commandProperties = { // Command 配置
// 設定操作時間為 100 毫秒
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "100")
},
fallbackMethod = "fallbackForGetUsers" // 設定 fallback 方法
)
@GetMapping("/user/list")
public Collection<User> getUsers() throws InterruptedException {
long executeTime = random.nextInt(200);
// 通過休眠來模拟執行時間
System.out.println("Execute Time : " + executeTime + " ms");
Thread.sleep(executeTime);
return userService.findAll();
}
為getUsers()添加fallback方法:
/**
* {@link #getUsers()} 的 fallback 方法
*
* @return 空集合
*/
public Collection<User> fallbackForGetUsers() {
return Collections.emptyList();
}
@EnableCircuitBreaker
實作服務調用方短路
@EnableCircuitBreaker
調整user-ribbon-client,為UserRibbonController增加擷取使用者清單,實際調用user-service-provider"/user/list"REST接口
增加具備負載均衡
RestTemplate
在
UserRibbonClientApplication
增加
RestTemplate
申明
/**
* 申明 具有負載均衡能力 {@link RestTemplate}
* @return
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
實作服務調用
/**
* 調用 user-service-provider "/user/list" REST 接口,并且直接傳回内容
* 增加 短路功能
*/
@GetMapping("/user-service-provider/user/list")
public Collection<User> getUsersList() {
return restTemplate.getForObject("http://" + providerServiceName + "/user/list", Collection.class);
}
激活
@EnableCircuitBreaker
調整UserRibbonClientApplication:
package com.segumentfault.spring.cloud.lesson8.user.ribbon.client;
import com.netflix.loadbalancer.IRule;
import com.segumentfault.spring.cloud.lesson8.user.ribbon.client.rule.MyRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
/**
* 引導類
*
* @author <a href="mailto:[email protected]">Mercy</a>
* @since 0.0.1
*/
@SpringBootApplication
@RibbonClient("user-service-provider") // 指定目标應用名稱
@EnableCircuitBreaker // 使用服務短路
public class UserRibbonClientApplication {
public static void main(String[] args) {
SpringApplication.run(UserRibbonClientApplication.class, args);
}
/**
* 将 {@link MyRule} 暴露成 {@link Bean}
*
* @return {@link MyRule}
*/
@Bean
public IRule myRule() {
return new MyRule();
}
/**
* 申明 具有負載均衡能力 {@link RestTemplate}
* @return
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
增加程式設計方式的短路實作
package com.segumentfault.spring.cloud.lesson8.user.ribbon.client.hystrix;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import org.springframework.web.client.RestTemplate;
import java.util.Collection;
import java.util.Collections;
/**
* User Ribbon Client HystrixCommand
*
* @author <a href="mailto:[email protected]">Mercy</a>
* @since 0.0.1
*/
public class UserRibbonClientHystrixCommand extends HystrixCommand<Collection> {
private final String providerServiceName;
private final RestTemplate restTemplate;
public UserRibbonClientHystrixCommand(String providerServiceName, RestTemplate restTemplate) {
super(HystrixCommandGroupKey.Factory.asKey(
"User-Ribbon-Client"),
100);
this.providerServiceName = providerServiceName;
this.restTemplate = restTemplate;
}
/**
* 主邏輯實作
*
* @return
* @throws Exception
*/
@Override
protected Collection run() throws Exception {
return restTemplate.getForObject("http://" + providerServiceName + "/user/list", Collection.class);
}
/**
* Fallback 實作
*
* @return 空集合
*/
protected Collection getFallback() {
return Collections.emptyList();
}
}
改造 UserRibbonController#getUsersList()
方法
UserRibbonController#getUsersList()
package com.segumentfault.spring.cloud.lesson8.user.ribbon.client.web.controller;
import com.segumentfault.spring.cloud.lesson8.domain.User;
import com.segumentfault.spring.cloud.lesson8.user.ribbon.client.hystrix.UserRibbonClientHystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.Collection;
/**
* 使用者 Ribbon Controller
*
* @author <a href="mailto:[email protected]">Mercy</a>
* @since 0.0.1
*/
@RestController
public class UserRibbonController {
/**
* 負載均衡器用戶端
*/
@Autowired
private LoadBalancerClient loadBalancerClient;
@Value("${provider.service.name}")
private String providerServiceName;
@Autowired
private RestTemplate restTemplate;
private UserRibbonClientHystrixCommand hystrixCommand;
/**
* 調用 user-service-provider "/user/list" REST 接口,并且直接傳回内容
* 增加 短路功能
*/
@GetMapping("/user-service-provider/user/list")
public Collection<User> getUsersList() {
return new UserRibbonClientHystrixCommand(providerServiceName, restTemplate).execute();
}
}
為生産做準備
Netflix Hystrix Dashboard
建立hystrix-dashboard工程
<dependencies>
<!-- 依賴 Hystrix Dashboard -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
</dependencies>
增加引導類
package com.segumentfault.spring.cloud.lesson8.hystrix.dashboard;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
/**
* Hystrix Dashboard 引導類
*
* @author <a href="mailto:[email protected]">Mercy</a>
* @since 0.0.1
*/
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardApplication.class, args);
}
}
增加application.properties
## Hystrix Dashboard 應用
spring.application.name = hystrix-dashboard
## 服務端口
server.port = 10000