04-Hystrix

服务熔断Hystrix

1. Hystrix是什么

分布式系统环境下,服务间类似依赖非常常见,一个业务调用通常依赖多个基础服务。如下图,对于同步调用,当库存服务不可用时,商品服务请求线程被阻塞,当有大批量请求调用库存服务时,最终可能导致整个商品服务资源耗尽,无法继续对外提供服务。并且这种不可用可能沿请求调用链向上传递,这种现象被称为雪崩效应。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VUVnZy2J-1671968360888)(images/42.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y7HsW098-1671968360889)(images/43.png)]

为了解决此问题,微服务架构中引入了一种叫熔断器的服务保护机制。

熔断器也有叫断路器,他们表示同一个意思,最早来源于微服务之父Martin Fowler的论文CircuitBreaker一文。“熔断器”本身是一种开关装置,用于在电路上保护线路过载,当线路中有电器发生短路时,能够及时切断故障电路,防止发生过载、发热甚至起火等严重后果。

微服务架构中的熔断器,就是当被调用方没有响应,调用方直接返回一个错误响应即可,而不是长时间的等待,这样避免调用时因为等待而线程一直得不到释放,避免故障在分布式系统间蔓延;

Spring Cloud Hystrix实现了熔断器、线程隔离等一系列服务保护功能。该功能也是基于Netflix的开源框架Hystrix实现的,该框架的目标在于通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Hystrix具备服务降级、服务熔断、线程和信号隔离、请求缓存、请求合并以及服务监控等强大功能。

2.引起雪崩效应常见场景

硬件故障:如服务器宕机,机房断电,光纤被挖断等

流量激增:如异常流量,重试加大流量等

缓存击穿:一般发生在应用重启,所有缓存失效时,以及短时间内大量缓存失效时。大量的缓存不命中,使请求直击后端服务,造成服务提供者超负荷运行,引起服务不可用

程序 BUG:如程序逻辑导致内存泄漏,JVM 长时间 FullGC 等

同步等待:服务间采用同步调用模式,同步等待造成的资源耗尽

3. Hystrix快速入门

当有服务调用的时候,才会出现服务雪崩,所以Hystrix常和OpenFeign,Ribbon一起出现。

在SpringCloud中使用熔断器Hystrix, 构建产品服务product-service,订单服务order-service

3.1创建注册中心eurkea-server

3.2设置eurke-server中的application的配置

server:
  port: 9100
spring:
  application:
    name: eureka-server
eureka:
  client:
    register-with-eureka: false #由于我们目前创建的应用是一个服务注册中心,而不是普通的应用,默认情况下,这个应用会向注册中心(也是它自己)注册它自己,设置为false表示禁止这种自己向自己注册的默认行为
    fetch-registry: false #由于我们目前创建的应用是一个服务注册中心,而不是普通的应用,默认情况下,这个应用会向注册中心(也是它自己)注册它自己,设置为false表示禁止这种自己向自己注册的默认行为

3.3产品服务product-service(提供者)提供的服务

@RequestMapping("/product")  
 public String queryProduct(){
   return "商品查询成功,小米笔记本,质量杠杠滴";
 }

3.4application文件配置内容

server:
  port: 8081
spring:
  application:
    name: product-service
eureka:
  client:
    service-url:
      defaultZone: http://localhost:9100/eureka

3.5创建消费者项目order-service中添加hystrix依赖

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

3.6在入口类@EnableCircuitBreaker注解开启断路器功能

也可以使用一个名为@SpringCloudApplication的注解代替主类上的三个注解;

@SpringCloudApplication
@EnableCircuitBreaker
@EnableDiscoveryClient
@EnableFeignClients 
public class OrderserviceApplication {

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

3.7.开启熔断保护

SpringCloud Fegin默认已为Feign整合了hystrix,所以添加Feign依赖后就不用在添加hystrix,

按照以下步骤开发:
(1)order-service中application文件中添加以下配置,开启hystrix

#在feign中开启熔断服务
feign:
    circuitbreaker:
        enabled: true

3.8自定义一个FeignClient接口的实现类

这个类就是熔断降级触发的逻辑代码

@Component
public class ProductFeignClientCallback  implements  ProductFeignClient{
    @Override //降级服务的实现
    public String queryProduct() {
        return "产品服务正在升级中,暂时不可用,请客官耐心等待....";
    }
}
/*fallback =ProductFeignImpl.class 熔断调用 实现类*/
@FeignClient(name = "product-service",fallback =ProductFeignClientCallback.class)
public interface ProductFeign {

    @GetMapping("/product")
    String queryProduct();

    @GetMapping("/product/{id}") //务必和原始服务提供者方法保持一致
    String detail(@PathVariable  Integer id);

    @PostMapping("/add")
    String addProduct(@RequestParam("id") Integer id,@RequestParam("name") String name);
}

3.9使用RestTemplate调用,实现熔断

启动类添加注解@EnableCircuitBreaker

   @GetMapping("/orderProduct")
    @HystrixCommand(fallbackMethod = "productFallback")
    public String orderProduct()
    {
        String url="http://product-service/product";
        return "产品订购完成:"+restTemplate.getForObject(url,String.class);
    }

    public String productFallback()
    {
        return "抢购的人太多了,请稍后再来...";
    }

注意:

熔断的方法名可以不需要和主方法名保持一致
但是错误的方法返回值类型必须和主方法返回值类型保持一致
错误错误的方法参数类型必须和主方法的参数类型保持一致
否则会报fallbackMethod找不到的异常

3.9在接口上添加熔断类型的注解

@FeignClient(name="product-service",fallback = ProductFeignClientCallback.class)
public interface ProductFeignClient {
}

3.10执行测试

(1)执行测试,当产品服务正常工作时,订单服务正确调用产品服务

(2)关闭产品服务,则执行消费者提供的熔断降级方法

4.常用配置

server:
    port: 8081
spring:
    application:
        name: consumer-service
eureka:
    client:
        service-url:
            defaultZone: http://localhost:8761/eureka/
        fetch-registry: true
        register-with-eureka: true
    instance:
        instance-id: ${spring.application.name}:${server.port}
        prefer-ip-address: true
feign:
    circuitbreaker:
        enabled: true
    client:
        config:
            default:
                connectTimeout: 5000
                readTimeout: 5000
                loggerLevel: BASIC
hystrix:
    command:
        default:
            execution:
                isolation:
                    strategy: THREAD  # 隔离方式 THREAD线程隔离集合和SEMAPHORE信号量隔离级别
                    thread:
                        timeoutInMilliseconds: 1000  # 调用超时时间
                    semaphore:
                        maxConcurrentRequests: 1000  # 信号量隔离是最大并发请求数
            circuitBreaker:
                requestVolumeThreshold: 3       #失败次数(阀值)
                errorThresholdPercentage: 60 #失败率
                sleepWindowInMilliseconds: 5000  # 断路器处理打开状态后 多久变成半开
            metrics:
                rollingStats:
                    timeInMilliseconds: 10000  # 统计窗口时间

5.隔离方式

hystrix的一项核心功能,就是所谓的资源隔离,资源隔离要解决的最核心的问题,就是将多个依赖服务的调用分别隔离到各自的资源池内。避免对某一个依赖服务的调用,因为依赖服务的接口调用的延迟或者失败,导致服务所有的线程资源全部耗费在这个服务的接口调用上。一旦某个服务的线程资源全部耗尽的话,可能就导致服务就会崩溃,甚至说这种故障会不断蔓延.hystrix支持两种方式的资源隔离

5.1线程池隔离(默认的方式)

隔离是通过线程池来做到的,也就是说他的隔离粒度是线程池。一个请求进来都经过一个线程池。

当前端发起请求过来到服务A或者B之后,服务A和服务B是通过线程池隔离的。服务A是否熔断,是否正常都和服务B无关。

他其实是一个异步编程,用线程池将后面的服务包裹了起来,至于服务内部tomcate的线程运行怎么样是无关的。他适合于绝大多数的场景,对于一些超时的场景都非常好用。但是既然是通过线程池来操作的,不可避免的就是线程之间的计算开销,以及线程上下文的切换,调度消耗。

适合绝大多数的场景。特别是 对其他服务的网络请求与调用存在 timeout 问题时。
如果当前请求的服务,需要耗时很长才能完成服务的响应,为了防止超时,可以使用的线程池杜绝服务因为超时出现问题。

5.2信号量隔离

线程隔离会带来线程开销,有些场景(比如无网络请求场景)可能会因为用开销换隔离得不偿失,为此hystrix提供了信号量隔离,当服务的并发数大于信号量阈值时将进入fallback 。

隔离是通过信号量来做到的。其实是一个计数器。一个请求进来就会减少一个信号,一个请求完成就会增加一个信号。信号量的调用是同步的,也就是说他会阻塞直到请求回来。

适合与对内部的一些比较复杂的业务逻辑的访问,而不是对外部依赖的访问。
像这种访问系统内部的代码,其实不涉及网络请求,那么只要做信号量的普通限流就可以了,因为不需要去捕获 timeout 类似的问题,程序在做 “算法+数据结构” 等操作时如果效率不是太高,并发量突然太高,因为比较微耗时,导致很多线程卡在这里的话,不太好,所以进行一个基本的资源隔离和访问,避免内部复杂的低效率的代码,导致大量的线程被hang住。

6.Hystrix服务熔断开启条件

熔断机制是应对雪崩效应的一种微服务链路保护机制,一般来说,每个服务都需要熔断机制的。如高压电路中,如果某个地方的电压过高,熔断器就会熔断,对电路进行保护。在微服务架构中,熔断机制也是起着类似的作用。

当链路的某个微服务不可用,或响应超时,或宕机,或异常时,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路。

熔断器开启的条件

当请求达到阀值时候:默认 10 秒内 20 次请求,当请求失败率达到默认 50% 的时,此时断路器将会开启,所有的请求都不会执行

断路器关闭的条件

当断路器开启 5 秒(默认)时,这时断路器是半开状态, 会允许其中一个请求执行

当链路的某个微服务不可用,或响应超时,或宕机,或异常时,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路。

熔断器开启的条件

当请求达到阀值时候:默认 10 秒内 20 次请求,当请求失败率达到默认 50% 的时,此时断路器将会开启,所有的请求都不会执行

断路器关闭的条件

当断路器开启 5 秒(默认)时,这时断路器是半开状态, 会允许其中一个请求执行

如果执行成功,则断路器会关闭;如果失败,则继续开启。循环重复这两个流程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

huangshaohui00

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值