九、Spring cloud服务短路(Hystrix)

  关于服务短路,首先介绍两个核心概念:

  • 服务容错:强调容忍错误,不至于整体故障
  • 服务降级:强调服务非强依赖,不影响主要流程

  这里的服务短路我们分为两种方式进行处理:

  • 传统处理方式(Spring Web MVC)
  • Hystrix

一、传统 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

你可能感兴趣的:(Spring,Cloud)