SpringCloud(三)——Hystrix

Hiystrix,熔断器,用于微服务系统中的保护中间件。

在微服务项目中,各个微服务相互调用,如果中间某个接口出现了异常,或是因为网络延迟、或是因为高并发下某个节点被阻塞而导致整个服务的资源耗尽,这样就可能会影响整个调用链的上游系统,出现服务雪崩现象。

针对服务雪崩,我们在这里可以采用要介绍的Hystrix组件,进行资源隔离、快速失败等处理方法,避免一直占用系统资源无法正常响应。

接下来看下如何使用:

一、依赖

    <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

三、使用

这里先看一下最简单的一个使用

这里在order-service服务中编写要调用的接口:

在启动类添加开启Hystrix的注解:

@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class OrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(MicroWebnoApplication.class,args);
    }
}

创建一个Service:

@Slf4j
@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private RestTemplate restTemplate;

    @Override
    @HystrixCommand(fallbackMethod = "handleError")
    public String hystrixTest() {
        return "hello";
    }

    public String handleError(){
        return "出错啦";
    }
}

hystrixTest方法中添加HystrixCommand注解,并指定出错的回调方法为下面的handleError

Controller:

@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private OrderService orderService;

    @GetMapping("/hystrix")
    public String hystrix(){
        return orderService.hystrixTest();
    }

然后在另一个web服务中添加一个调用上面order-service服务的接口:

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

    @GetMapping("/hystrix")
    public String hystrix(){
        String result = restTemplate.getForObject("http://ORDER-SERVICE/order/hystrix", String.class);
        return result;
    }

这里仍然用前面介绍Ribbon时用的RestTemplate进行服务调用:

@Configuration
public class RestTemplateConfig {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

加上两个Eureka服务端,一个4个服务实例:

在这里插入图片描述

接下来我们就请求/user/hystrix,它就会调用order-service服务端的接口。

在这里插入图片描述

返回hello。

此时我们修改之前的service代码,让它sleep10s,这样就会因为超时进入出错的回调方法处理:

 @HystrixCommand(fallbackMethod = "handleError")
    public String hystrixTest() {
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "hello";
    }

重新请求:

在这里插入图片描述

返回失败回调信息

四、隔离策略

Hystrix共有两种隔离策略:线程池隔离信号量隔离

4.1 线程隔离策略

该策略为默认策略。

配置如下:

   @Override
    @HystrixCommand(fallbackMethod = "handleError",
            commandKey = "hystrixTest",
            groupKey = "hystrixTestGroup",
            commandProperties = {
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1000000"),
                    @HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"),//指定隔离策略
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000000000")
            },
            threadPoolKey = "hystrixTestThreadPool",
            threadPoolProperties = {
                @HystrixProperty(name = "coreSize", value = "5")
    })
    public String hystrixTest() {
        return "a";
    }

以上相关属性说明如下:

commandKey,当前command的名称

groupKey:当前command所在的组。默认情况下根据group定义一个线程池,即同一个组的请求进入同一个线程池;也根据group聚合一些监控和报警信息等

execution.isolation.thread.timeoutInMilliseconds:隔离策略为THREAD时,执行的超时时间,单位ms

execution.isolation.strategy:指定隔离策略。THREAD或SEMAPHORE

threadPoolKey:线程池的key,默认为group的名称

coreSize:线程池大小。默认10

注意:

如果同时配置了groupKeythreadPoolKey,则相同threadPoolKey的使用同一个线程池;

如果只配置了groupKey,则相同groupKey的使用一个线程池。

通过HystrixCommand注解的配置,我们可以为该接口的调用分配一个线程池,该线程池大小默认为10,这样可以完全的隔离当前的业务代码,请求可以快速返回,每请求一个都创建一个线程处理,当线程数超过线程池容量,后面的用户请求就走失败回调。

我们使用Jmeter压测,1s并发6个请求,会发现5个成功处理,另外一个走了fallback方法:

在这里插入图片描述

同时分别在HystrixCommand注解的方法中和controller中打印当前线程的名字:

在这里插入图片描述

会发现两个地方的线程完全不一样,因此可以确定线程池隔离策略时Hystrix用的是专门的线程池单独开启的额外的线程。

坏处:

需要大量计算,耗费资源。

但是处理快

4.2 信号量隔离策略

    @HystrixCommand(fallbackMethod = "handleError",
            commandKey = "hystrixsemaphore",
            groupKey = "hystrixTestGroup-one",
            commandProperties = {
                    @HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE"),
                    @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests",value = "10"),
            },
            threadPoolKey = "SemaphoreThreadPool",
            threadPoolProperties = {
                    @HystrixProperty(name = "coreSize", value = "10")
            })

如上,修改为信号量隔离策略:

  • execution.isolation.semaphore.maxConcurrentRequests 配置信号量的大小,达到最大并发信号量时,后续请求被拒

此时我们再去并发请求一下:
在这里插入图片描述

发现用户线程和业务service的线程是相同的线程池,没有创建额外的线程

信号量隔离是采用一个全局变量来控制并发量,一个请求过来全局变量加 1,单加到跟配置 中的大小相等是就不再接受用户请求了。

因此信号量相比线程池节省了大量资源,但是请求处理的速度相对就慢了些。

4.3 HystrixCommand属性详解

参考官方:

HystrixCommand注解属性配置信息官方详解

/**
     * Command属性
     * execution.isolation.strategy  执行的隔离策略
     * THREAD 线程池隔离策略  独立线程接收请求
     * SEMAPHORE 信号量隔离策略 在调用线程上执行
     * <p>
     * execution.isolation.thread.timeoutInMilliseconds  设置HystrixCommand执行的超时时间,单位毫秒
     * execution.timeout.enabled  是否启动超时时间,true,false
     * execution.isolation.semaphore.maxConcurrentRequests  隔离策略为信号量的时候,该属性来配置信号量的大小,最大并发达到信号量时,后续请求被拒绝
     * <p>
     * circuitBreaker.enabled   是否开启断路器功能
     * circuitBreaker.requestVolumeThreshold  该属性设置在滚动时间窗口中,断路器的最小请求数。默认20,如果在窗口时间内请求次数19,即使19个全部失败,断路器也不会打开
     * circuitBreaker.sleepWindowInMilliseconds    改属性用来设置当断路器打开之后的休眠时间,休眠时间结束后断路器为半开状态,断路器能接受请求,如果请求失败又重新回到打开状态,如果请求成功又回到关闭状态
     * circuitBreaker.errorThresholdPercentage  该属性设置断路器打开的错误百分比。在滚动时间内,在请求数量超过circuitBreaker.requestVolumeThreshold,如果错误请求数的百分比超过这个比例,断路器就为打开状态
     * circuitBreaker.forceOpen   true表示强制打开断路器,拒绝所有请求
     * circuitBreaker.forceClosed  true表示强制进入关闭状态,接收所有请求
     * <p>
     * metrics.rollingStats.timeInMilliseconds   设置滚动时间窗的长度,单位毫秒。这个时间窗口就是断路器收集信息的持续时间。断路器在收集指标信息的时会根据这个时间窗口把这个窗口拆分成多个桶,每个桶代表一段时间的指标,默认10000
     * metrics.rollingStats.numBuckets   滚动时间窗统计指标信息划分的桶的数量,但是滚动时间必须能够整除这个桶的个数,要不然抛异常
     * <p>
     * requestCache.enabled   是否开启请求缓存,默认为true
     * requestLog.enabled 是否打印日志到HystrixRequestLog中,默认true
     *
             * @HystrixCollapser 请求合并
     * maxRequestsInBatch  设置一次请求合并批处理中允许的最大请求数
     * timerDelayInMilliseconds  设置批处理过程中每个命令延迟时间
     * requestCache.enabled   批处理过程中是否开启请求缓存,默认true
     * <p>
     * threadPoolProperties
     * threadPoolProperties 属性
     * coreSize   执行命令线程池的最大线程数,也就是命令执行的最大并发数,默认10
     */

五、服务降级

上面的几个例子,都遇到了请求失败的情况,我们采取的是使用fallbackMethod = "handleError"交给失败处理的回调方法处理,这就是服务的降级处理。

需要注意的是,降级的方法和业务的方法返回值必须一样。

降级中的处理我们可以结合具体的业务处理,比如可以不直接返回失败信息,而是再次请求。

六、Hystrix健康监控

我们只要引入dashboard依赖即可开,其提供了专门的监控页:

<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>

6.2 配置

server.port=10000
# 暴露监控端点
management.endpoints.web.exposure.include=*

6.3 开启注解

接着在启动类开启监控的注解:

@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardApplication {
    public static void main(String[] args) {
        SpringApplication.run(HystrixDashboardApplication.class,args);
    }
}

监控的界面就是:

http://localhost:10000/hystrix

我们在上面输入要监控的有Hystrix的服务地址即可:

比如前面使用的order服务

http://localhost:8086/actuator/hystrix.stream

需要注意的是,要监控的服务要添加hystrix和actuator依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

在启动类添加@EnableCircuitBreaker注解。

这样被监控的服务才具备/hystrix.stream接口。再配置下监控接口:

management.endpoint.health.show-details=always
management.endpoint.shutdown.enabled=true
##hystrix.stream  开放所有的监控接口
management.endpoints.web.exposure.include=*

第一次进去的时候可能会遇到一只loading的情况,这时请求下目标服务即可。
在这里插入图片描述

当前,不难发现这样只能监控单一服务,而想一次监控多个服务需要使用Turbine。

6.4 Turbine环境搭建

添加依赖:

  <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-turbine</artifactId>
        </dependency>

启动类再添加个@EnableTurbine注解。

配置添加如下信息:

turbine.app-config=order-service,web-no
# 集群名字
turbine.cluster-name-expression="my-dashboard"
# 服务通过host和port区别,默认就是true
turbine.combine-host-port=true

启动后先访问http://localhost:10000/turbine.stream

然后监控该接口即可

在这里插入图片描述

七、熔断

熔断触发必须满足三个条件:

  1. metrics.rollingStats.timeInMilliseconds

    设置滚动时间窗大小,单位毫秒,默认10000。这个时间窗口就是断路器收集信息的最小单元。断路器在收集指标信息的时会根据这个时间窗口把这个窗口拆分成多个桶,每个桶代表一段时间的指标。

  2. circuitBreaker.requestVolumeThreshold

    设置在滚动时间窗口中,断路器的最小请求数。默认20。如果在窗口时间内请求此时超过20,就达到了熔断的当前条件

  3. circuitBreaker.errorThresholdPercentage

    设置断路器打开的错误率。默认50%。

    在滚动时间内,在请求数量超过circuitBreaker.requestVolumeThreshold,如果错误请求数占比超过该比例,断路器就为打开状态

出现熔断后,后面的请求就会走降级的方法,而不会走后面的服务。

简单的配置下:

 private static final AtomicInteger count=new AtomicInteger(0);
    @Override
    @HystrixCommand(fallbackMethod = "handleError",
            commandKey = "hystrixsemaphore",
            groupKey = "hystrixTestGroup-one",
            commandProperties = {
                    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
                    @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
                    @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "10000"),
                    @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "5"),
                    @HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE"),
                    @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "10"),
            },
            threadPoolKey = "SemaphoreThreadPool",
            threadPoolProperties = {
                    @HystrixProperty(name = "coreSize", value = "10")
            })
    public String hystrixsemaphore() {
        log.info("orderservice-hystrixsemaphore:" + Thread.currentThread().getName());
        if (count.getAndIncrement()%2==0){
            throw new RuntimeException("error");
        }
        return "哈哈";
    }

然后不断的请求该接口,10s内请求数大于10个且出错率超过50%时,熔断器就会打开:

在这里插入图片描述

等待一会,再次请求成功时,熔断器就关闭了:

在这里插入图片描述

熔断器的三个状态

  1. 关闭状态:用户请求是可以到达服务提供方的
  2. 开启状态 用户请求直接走降级方法
  3. 半开状态 当 hystrix 熔断器开启时,过一段时间后,熔断器就会由开启状态变成半开状态。

半开状态期间的熔断器是可以接受用户请求并把请求传递给服务提供方,其最终状态由该远程调用的结果决定:

远程调用成功:半开 ----> 关闭状态

远程调用失败:半开 -----> 打开状态

熔断器的使用建议只在并发高的方法上使用。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Cloud是一个用于构建分布式系统的开发工具集合,它提供了许多有用的组件,包括Zuul、Ribbon、Hystrix和@FeignClient。 Zuul是Spring Cloud中的网关服务,它可以将各个微服务的请求路由到相应的微服务上。Zuul具有负载均衡和过滤器等功能,可以对请求进行拦截和处理。 Ribbon是一个负载均衡器,它可以根据负载情况将请求分发给不同的微服务实例。Ribbon可以与Eureka等注册中心配合使用,动态地获取可用的服务实例列表,并根据一定的负载均衡策略选择合适的实例。 Hystrix是一个容错和延迟容忍的库,可以帮助我们构建稳定的分布式系统。它可以防止由于某一微服务的故障或延迟而导致整个系统的崩溃。通过为每个外部服务的调用添加断路器,Hystrix可以在外部服务不可用时提供备选方案,并且可以对外部服务的调用进行监控和度量。 @FeignClient是一个用于声明式REST客户端的注解。通过在接口中添加@FeignClient注解,并指定要访问的微服务名称,我们可以方便地进行REST调用。Feign会根据接口定义自动生成实现类,并将请求发送到相应的微服务。 综上所述,Spring Cloud中的Zuul、Ribbon、Hystrix和@FeignClient是用于构建分布式系统的重要组件。它们可以帮助我们解决微服务架构中的路由、负载均衡、容错和服务间调用等问题。利用这些组件,我们可以更方便地构建可靠、高效的分布式系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值