关于服务短路,首先介绍两个核心概念:
这里的服务短路我们分为两种方式进行处理:
一、传统 Spring Web MVC
以 web 工程为例:
(一)创建 DemoRestController
/** * Demo:随机数超过100,即判定超时 */
@RestController
public class DemoRestController {
private final static Random RANDOM = new Random();
@GetMapping("/timeout")
public Integer testTimeout() throws TimeoutException {
int randomNum = RANDOM.nextInt(200);
if (randomNum >= 100){
throw new TimeoutException("超时了");
}
return randomNum;
}
}
(二)超时异常处理
通过 DemoRestControllerAdvice 实现
/** * 注解@RestControllerAdvice:继承自 @ControllerAdvice,用于拦截Controller, * 而 @RestControllerAdvice 专门用于拦截 Rest 请求关联的 Controller。 * 类似于 AOP */
@RestControllerAdvice
public class DemoRestControllerAdvice {
/** * 注解 @ExceptionHandler:values 对象为,需要处理的异常类型 */
@ExceptionHandler(TimeoutException.class)
public String handleException(Throwable throwable){
return throwable.getMessage();
}
}
二、Spring Cloud Netflix Hystrix
Hystrix 既可以在服务端实现短路,也可以在客户端实现短路!!
(一)增加 Hystrix 依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrixartifactId>
dependency>
(二)使用 @EnableHystrix
实现 服务端(服务提供方) 短路
以下操作是在user-service-provider
项目中进行。关于user-service-provider
项目,我们复用 八、Spring cloud负载均衡(Ribbon)之源码解析中的项目。
1、激活 Hystrix
/** * 注解 @EnableHystrix:激活 Hystrix * @author 咸鱼 * @date 2018/11/12 18:44 */
@EnableHystrix
@SpringBootApplication
public class UserServiceProviderApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceProviderApplication.class, args);
}
}
2、通过 @HystrixCommand
实现 服务提供方 短路、并进行短路处理
增加 getUsers() 方法到 UserServiceProviderController
/** * 获取所有用户列表 */
@HystrixCommand(
//Command 配置
commandProperties = {
//设置超时时间为 100ms
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "100")
},
//设置熔断方法(PS:当异常发生后的处理方法)
fallbackMethod = "fallbackForGetUsers"
)
@GetMapping("/user/list")
public List<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 List<User> fallbackForGetUsers(){
return Collections.emptyList();
}
(三)使用 @EnableCircuitBreaker
实现 客户端(服务调用方) 短路
客户端(服务调用方) 短路的基本逻辑:若客户端在超时时间内没有得到响应,将触发客户端熔断。
以下操作是在 user-ribbon-client
项目中进行。关于 user-ribbon-client
项目,我们复用 八、Spring cloud负载均衡(Ribbon)之源码解析中的项目。
为 UserRibbonController
增加获取用户列表,实际调用 user-service-provider
模块的 /user/list
REST 接口。
1、在 UserRibbonClientApplication
增加具备负载均衡能力的 RestTemplate
/** * 申明 具有负载均衡能力 {@link RestTemplate} */
@LoadBalanced
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
2、实现远程服务调用
@RestController
public class UserRibbonController {
@Value("${service.provider-name}")
private String serviceProviderName;
private RestTemplate restTemplate;
@Autowired
public UserRibbonController(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
/** * 调用 user-service-provider "/user/list" REST 接口,并且直接返回内容 * 增加 短路功能 */
@GetMapping("/user/list")
public List<User> getUserList(){
return restTemplate.getForObject("http://" + serviceProviderName + "/user/list", List.class);
}
}
3、激活 @EnableCircuitBreaker
调整 UserRibbonClientApplication
/** * 注解 @RibbonClient:激活 Ribbon * 注解 @EnableCircuitBreaker:激活 服务短路 */
@EnableCircuitBreaker
@SpringBootApplication
@RibbonClient("user-service-provider")//指定目标应用名称
public class UserRibbonClientApplication {
....
}
4、增加编程方式的短路实现(和前面使用 @HystrixCommand
注解相对应,是两种不同的实现方式,最终效果都是一样)
/** * User Ribbon Client HystrixCommand * @author 咸鱼 * @date 2018/11/13 19:40 */
public class UserRibbonClientHystrixCommand extends HystrixCommand<Collection> {
private final String serviceProviderName;
private final RestTemplate restTemplate;
public UserRibbonClientHystrixCommand(String serviceProviderName, RestTemplate restTemplate) {
//参数一:向父构造器传递一个Group 参数二:超时时间
super(HystrixCommandGroupKey.Factory.asKey("user-ribbon-client"),
100);
this.serviceProviderName = serviceProviderName;
this.restTemplate = restTemplate;
}
/** * 执行的业务逻辑(这里面会附带上熔断:若客户端不能在100ms内得到响应,将触发熔断) * @return 执行结果 */
@Override
protected Collection run() throws Exception {
return restTemplate.getForObject("http://" + serviceProviderName + "/user/list",
List.class);
}
/** * Fallback 实现(短路处理) * @return 空 */
@Override
protected Collection getFallback() {
return Collections.emptyList();
}
}
5、改造 UserRibbonController#getUserList()
方法
/** * 用户 Ribbon Controller * @author 咸鱼 * @date 2018/11/11 17:59 */
@RestController
public class UserRibbonController {
@Value("${service.provider-name}")
private String serviceProviderName;
private RestTemplate restTemplate;
@Autowired
public UserRibbonController(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
/** * 调用 user-service-provider "/user/list" REST 接口,并且直接返回内容 * 增加 短路功能 */
@GetMapping("/user/list")
public Collection<User> getUserList(){
//必须每次调用,都重新创建,否则会报错!!!
return new UserRibbonClientHystrixCommand(serviceProviderName, restTemplate).execute();
}
}
三、为生产准备 Nertflix Hystrix Dashboard
(一)创建 hystrix-dashboard 工程
(二)增加依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboardartifactId>
dependency>
(三)增加引导类
/** * 注解 @EnableHystrixDashboard:激活 HystrixDashboard */
@EnableHystrixDashboard
@SpringBootApplication
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
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
(五)启动工程,访问 Hystrix 主页
主页地址:http://localhost:10000/hystrix