SpringCloud之Hystrix使用篇

1.环境准备

我们继续使用《SpringCloud之Ribbon使用篇》里面集群环境或者是使用《SpringCloud之Eureka使用篇》集群环境。
1.首先我这里有spring-cloud-parent pom工程
2.spring-cloud-eureka-server Eureka Server 子工程 这里端口我们使用90开头
我们这里集群由两个服务实例组成,分别是9090与9091端口
3.spring-cloud-user-service-consumer 用户服务 (也就是咱们的服务消费者)这里端口我们使用80 开头
4.spring-cloud-order-service-provider 订单提供服务 (服务提供者) 这里端口我们使用70开头
我们订单服务提供着也由两个实例组成,分别是7070与7071
如果不想使用该环境,可以根据架构图来搭建自己的环境,这里我使用的spring-cloud版本是Greenwich.RELEASE。
在这里插入图片描述

2.基础用法

2.1 服务调用者

这里 spring-cloud-user-service-consumer 用户服务作为服务调用着我们需要对它改造

2.1.1 添加Hystrix依赖

我们需要对spring-cloud-user-service-consumer 服务pom.xml中添加Hystrix熔断器依赖

 <dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
 </dependency>
2.1.2 项目启动文件

也就是我们的xxxApplication.java 文件添加@EnableHystrix或者是@EnableCircuitBreaker 表示启用熔断器
在这里插入图片描述

2.1.3 controller

在我们需要熔断的方法上面添加@HystrixCommand 注解,然后并提供降级方案,也就是编写fallback方法。
这里我就在UserCenterController#getTodayStatistic 方法上添加熔断信息。

@RestController
@RequestMapping("/user/data")
public class UserCenterController {
    @Autowired
    private RestTemplate restTemplate;
    @GetMapping("/getTodayStatistic/{id}")
    @HystrixCommand(
            fallbackMethod = "getTodayStatisticFallback",// 服务降级方法

            // 使用commandProperties 可以配置熔断的一些细节信息
            commandProperties = {

                    // 类似kv形式
                    //这里这个参数意思是熔断超时时间2s,表示过了多长时间还没结束就进行熔断
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000")
            }
    )
    public Integer getTodayStatistic(@PathVariable("id") Integer id){
        String url  ="http://spring-cloud-order-service-provider/order/data/getTodayFinishOrderNum/"+id;
        return restTemplate.getForObject(url, Integer.class);
    }
    // 服务降级方法 ,这里参数与返回值需要原方法保持一直
    public Integer getTodayStatisticFallback(Integer id){
        return -1;
    }
}

这里我对getTodayStatistic 添加熔断信息,如果2s没有处理完就进行熔断,然后服务降级方法是getTodayStatisticFallback,熔断了之后接着执行getTodayStatisticFallback方法。

2.2 服务提供者

我这里是spring-cloud-order-service-provider ,然后使用springboot spring.profiles特性分别启动7070,7071端口这两个实例。
这里为了演示方便,我需要把7070 端口的服务方法 进行睡眠,然后执行时长要超过咱们的熔断超时时长。

2.2.1 controller

7070 端口的服务方法 进行睡眠

@RestController
@RequestMapping("/order/data")
public class OrderStatisticServiceController {
    @Value("${server.port}")
    private Integer port;
    /**
     * 根据用户id获取今日完单数
     * @param id 用户ID
     * @return  完单数
     */
    @GetMapping("/getTodayFinishOrderNum/{id}")
    public Integer getTodayFinishOrderNum(@PathVariable("id") Integer id){
        
        
        if (port==7070){
            try {// 睡眠10s
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return port;
    }
}

2.3 启动项目验证

2.3.1 启动Eureka 集群

启动Eureka集群,端口9090,9091
在这里插入图片描述

2.3.2 启动服务提供者

启动服务提供者,分别是7070,7071,我们看到服务提供者已经注册上去了
在这里插入图片描述

2.3.3 启动服务提供者并测试

在这里插入图片描述
第一次调用
在这里插入图片描述
我们可以看到第一次就调用到了7070端口,发生了熔断,进行了服务降级。
第二次调用
在这里插入图片描述
由于使用Ribbon进行负载均衡第二次调用到了7071端口的服务提供者。

3.舱壁模式

3.1 什么是舱壁模式

首先舱壁模式就是保证微服务健壮性的一种模式,舱壁模式通过隔离每个工作负载或服务的关键资源,如连接池、内存和CPU来防止由一个服务引起的级联故障来增加系统的弹性。例如我们的船舱,我们船就一个船舱的话,如果破了一个洞,然后海水就慢慢灌满了整艘船,到最后船就沉下去了,但是如果我们将整艘船隔成若干个船舱,像下图这样,如果船破了一个洞,海水顶多会灌满一个船舱,不会导致整艘船沉下去。
在这里插入图片描述

3.2 舱壁模式在Hystrix的应用

在Hystrix中,使用了线程池隔离策略,Hystrix中有一个线程池(默认是10个线程),然后供所有添加了@HystrixCommand注解的方法使用,如果那些方法的请求超过10个,其他请求就得等待或者拒绝,像下图这样。
在这里插入图片描述
这里如果不进行设置的话,默认会共用一个线程池,这样很容易出问题,比如说我请求量很大,然后方法A 把这10个线程全用了,而且A后面调用的服务方法处理很慢,然后我方法B跟方法C就没有线程用了,然后就得等待,然后超时被熔断,这里并不是我们后面的服务不可用,不是服务1的某个方法,服务2的某个方法不可用,而是服务调用者压根没有线程去处理这些请求。
然后为了避免这种情况的发生,Hystrix没有通过增加线程数来处理这个问题,而是对每一个添加@HystrixCommand 注解的方法创建线程池来进行隔离,就算是某个方法出了问题也不会影响到其他方法,这就是舱壁模式在Hystrix的应用
在这里插入图片描述
这样方法的调用就隔离开了,两个互不影响。

3.3 Hystrix线程池隔离配置
3.3.1 确认多个@HystrixCommand方法共用一个线程池

在使用线程池隔离配置之前,我们先看下Hystrix默认线程池情况。我们把服务调用者也就是用户服务的controller修改一下,将getTodayStatistic方法cp一份,服务降级的方法也cp一份

@RestController
@RequestMapping("/user/data")
public class UserCenterController {
    @Autowired
    private RestTemplate restTemplate;
    @GetMapping("/getTodayStatistic/{id}")
    @HystrixCommand(
            fallbackMethod = "getTodayStatisticFallback",// 服务降级方法

            // 使用commandProperties 可以配置熔断的一些细节信息
            commandProperties = {

                    // 类似kv形式
                    //这里这个参数意思是熔断超时时间2s,表示过了多长时间还没结束就进行熔断
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000")
            }
    )
    public Integer getTodayStatistic(@PathVariable("id") Integer id){
        String url  ="http://spring-cloud-order-service-provider/order/data/getTodayFinishOrderNum/"+id;
        return restTemplate.getForObject(url, Integer.class);
    }
    // 服务降级方法 ,这里参数与返回值需要原方法保持一直
    public Integer getTodayStatisticFallback(Integer id){
        return -1;
    }
    @GetMapping("/getTodayStatisticA/{id}")
    @HystrixCommand(
            fallbackMethod = "getTodayStatisticFallbackA",// 服务降级方法
            // 使用commandProperties 可以配置熔断的一些细节信息
            commandProperties = {
                    // 类似kv形式
                    //这里这个参数意思是熔断超时时间2s,表示过了多长时间还没结束就进行熔断
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000")
            }
    )
    public Integer getTodayStatisticA(@PathVariable("id") Integer id){
        String url  ="http://spring-cloud-order-service-provider/order/data/getTodayFinishOrderNum/"+id;
        return restTemplate.getForObject(url, Integer.class);
    }
    // 服务降级方法 ,这里参数与返回值需要原方法保持一直
    public Integer getTodayStatisticFallbackA(Integer id){
        return -1;
    }
}

然后我们使用postman进行批量请求,将这两个连接放到一个ccollection下面,一定要保存下子,然后重启服务。
在这里插入图片描述
接着点击collection run
在这里插入图片描述
修改Iterations,让它执行20次
在这里插入图片描述
执行完成后,我们使用jstack查看一下服务调用者的hystrix线程,我们看到只有10个线程。
在这里插入图片描述

3.3.2 配置一个方法一个线程池

我可以使用@HystrixCommand 注解里面的参数threadPoolKey 来定义使用的线程池key,这个需要唯一,不唯一的话共用。然后使用threadPoolProperties属性来配置线程池的一些细节参数
我们修改下服务调用者也就是用户服务的controller

@RestController
@RequestMapping("/user/data")
public class UserCenterController {
    @Autowired
    private RestTemplate restTemplate;


    @GetMapping("/getTodayStatistic/{id}")
    @HystrixCommand(
            // 线程池标识
            threadPoolKey = "getTodayStatistic",
            threadPoolProperties = {
                    @HystrixProperty(name="coreSize",value = "3"),  // 这个就是咱们那个线程池core线程核心数
                    @HystrixProperty(name="maxQueueSize",value="100") //这个是队列大小
            },
            fallbackMethod = "getTodayStatisticFallback",// 服务降级方法

            // 使用commandProperties 可以配置熔断的一些细节信息
            commandProperties = {

                    // 类似kv形式
                    //这里这个参数意思是熔断超时时间2s,表示过了多长时间还没结束就进行熔断
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000")
            }
    )
    public Integer getTodayStatistic(@PathVariable("id") Integer id){
        String url  ="http://spring-cloud-order-service-provider/order/data/getTodayFinishOrderNum/"+id;
        return restTemplate.getForObject(url, Integer.class);
    }
    // 服务降级方法 ,这里参数与返回值需要原方法保持一直
    public Integer getTodayStatisticFallback(Integer id){
        return -1;
    }
    @GetMapping("/getTodayStatisticA/{id}")
    @HystrixCommand(
            // 线程池标识
            threadPoolKey = "getTodayStatisticA",
            threadPoolProperties = {
                    @HystrixProperty(name="coreSize",value = "2"),  // 这个就是咱们那个线程池core线程核心数
                    @HystrixProperty(name="maxQueueSize",value="100") //这个是队列大小
            },
            fallbackMethod = "getTodayStatisticFallbackA",// 服务降级方法

            // 使用commandProperties 可以配置熔断的一些细节信息
            commandProperties = {

                    // 类似kv形式
                    //这里这个参数意思是熔断超时时间2s,表示过了多长时间还没结束就进行熔断
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000")
            }
    )
    public Integer getTodayStatisticA(@PathVariable("id") Integer id){
        String url  ="http://spring-cloud-order-service-provider/order/data/getTodayFinishOrderNum/"+id;
        return restTemplate.getForObject(url, Integer.class);
    }
    // 服务降级方法 ,这里参数与返回值需要原方法保持一直
    public Integer getTodayStatisticFallbackA(Integer id){
        return -1;
    }
}

重启一下服务调用者项目,然后使用postman进行批量请求,跑完之后使用jstack看线程情况。
在这里插入图片描述
这里我们就可以看到,咱们getTodayStatistic 是有3个线程的,然后getTodayStatisticA是有2个线程的。

猜你喜欢

转载自blog.csdn.net/yuanshangshenghuo/article/details/106986372