Hystrix断路器使用

1.Hystrix简介

Hystrix,英文意思是豪猪,全身是刺,看起来就不好惹,是一种保护机制。Hystrix也是Netflix公司的一款组件。
主页:https://github.com/Netflix/Hystrix/
Hystix是Netflix开源的一个延迟和容错库,用于隔离访问远程服务、第三方库,防止出现级联失败。

2.服务雪崩

微服务中,服务间调用关系错综复杂,一个请求,可能需要调用多个微服务接口才能实现,会形成非常复杂的调用链路:
在这里插入图片描述
如图,一次业务请求,需要调用A、P、H、I四个服务,这四个服务又可能调用其它服务。
如果此时,某个服务出现异常:

在这里插入图片描述
例如微服务I发生异常,请求阻塞,用户不会得到响应,则tomcat的这个线程不会释放,于是越来越多的用户请求到来,越来越多的线程会阻塞:
在这里插入图片描述
服务器支持的线程和并发数有限,请求一直阻塞,会导致服务器资源耗尽,从而导致所有其它服务都不可用,形成雪崩效应。
这就好比,一个汽车生产线,生产不同的汽车,需要使用不同的零件,如果某个零件因为种种原因无法使用,那么就会造成整台车无法装配,陷入等待零件的状态,直到零件到位,才能继续组装。 此时如果有很多个车型都需要这个零件,那么整个工厂都将陷入等待的状态,导致所有生产都陷入瘫痪。一个零件的波及范围不断扩大。

3.Hystrix的几个重要概念

(1)服务降级:
服务器忙碌或者网络拥堵时,不让客户端等待并立刻返回一个友好提示,fallback(备选方案)。
触发服务降级的情况:
在这里插入图片描述

(2)服务熔断:
在这里插入图片描述

(3)服务限流
在这里插入图片描述

4.Hystrix案例

1.创建提供者模块

1.新建提供者cloud-provider-hystrix-payment8001模块:
2.引入pom依赖

<dependencies>
    <!-- hystrix -->
<!--
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
-->
    <!--eureka-client-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <groupId>com.krisswen.cloud</groupId>
        <artifactId>cloud-api-commons</artifactId>
        <version>${project.version}</version>
    </dependency>
</dependencies>

3.编写配置文件

server:
  port: 8001
spring:
  application:
    name: cloud-payment-service  #服务名称
eureka:
  client:
    # 注册进 Eureka 的服务中心
    register-with-eureka: true
    # 检索 服务中心 的其它服务
    fetch-registry: true
    service-url:
      # 设置与 Eureka Server 交互的地址
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/

4.编写启动类

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

5.编写service

@Service
public class PaymentService {

    /**
     * 可以正常访问的方法
     * @param id
     * @return
     */
    public String paymentInfo_Ok(Integer id){
        return "线程池:" + Thread.currentThread().getName() + "  ,paymentInfo_OK,id:" + id;
    }

    /**
     超时访问的方法
     */
    public String paymentInfo_Timeout(Integer id){
        int interTime = 3;
        try{
            TimeUnit.SECONDS.sleep(interTime);
        }catch (Exception e){
            e.printStackTrace();
        }
        return "线程池:" + Thread.currentThread().getName() + "  ,paymentInfo_Timeout,id:" + id + "耗时" + interTime + "秒钟";
    }
}

6.编写controller

@RestController
@Slf4j
public class PaymentController {

    @Autowired
    PaymentService paymentService;

    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/payment/hystrix/{id}")
    public String paymentInfo_OK(@PathVariable("id")Integer id){
        log.info("paymentInfo_OKKKKOKKK");
        return paymentService.paymentInfo_Ok(id);
    }

    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfo_Timeout(@PathVariable("id")Integer id){
        log.info("paymentInfo_timeout");
        return paymentService.paymentInfo_Timeout(id);
    }
}
// localhost:8001/payment/hystrix/1   localhost:8001/payment/hystrix/timeout/1

7.启动测试
在这里插入图片描述
在这里插入图片描述

2.创建消费者模块

1.定义cloud-customer-feign-hystrix-order80模块
2.导入pom依赖

<dependencies>
    <!-- hystrix -->
   <!-- <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>-->
    <!-- OpenFeign -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <!--eureka-client-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <groupId>com.krisswen.cloud</groupId>
        <artifactId>cloud-api-commons</artifactId>
        <version>${project.version}</version>
    </dependency>
</dependencies>

3.编写配置文件

server:
  port: 80
spring:
  application:
    name: cloud-customer-feign-hystrix-service
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/

4.编写启动类

@SpringBootApplication
@DiscoveryClient
@EnableFeignClients //开启Feign客户端
public class HystrixOrderApplication80 {
    public static void main(String[] args) {
        SpringApplication.run(HystrixOrderApplication80.class,args);
    }
}

5.编写feign客户端

@Component
@FeignClient(value = "cloud-payment-service")
public interface OrderService {

    @GetMapping("/payment/hystrix/{id}")
    public String paymentInfo_OK(@PathVariable("id")Integer id);

    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfo_Timeout(@PathVariable("id")Integer id);
}

6.编写controller

@RestController
@Slf4j
public class OrderController {

    @Autowired
    OrderService orderService;

    @GetMapping("/consumer/payment/hystrix/{id}")
    public String paymentInfo_OK(@PathVariable("id")Integer id){
        log.info("paymentInfo_OKKKKOKKK");
        return orderService.paymentInfo_OK(id);
    }

    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    public String paymentInfo_Timeout(@PathVariable("id")Integer id){
        log.info("paymentInfo_timeout");
        return orderService.paymentInfo_Timeout(id);
    }
}
//localhost/consumer/payment/hystrix/1

7.测试
在这里插入图片描述
8.使用jmeter压测工具模拟高并发
要去官网下载一个新东西 JMeter 压力测试器。
在这里插入图片描述
测试可见,当启动高并发测试时,消费者访问也会变得很慢,甚至出现超时报错。
解决思路:
对方(8001)服务超时了,调用者(80)不能一直卡死等待,必须有服务降级
对方(8001)服务down机了,调用者(80)不能一直卡死等待,必须有服务降级
对方(8001)服务ok,调用者(80)自己出现故障或有自我要求(自己的等待时间小于服务提供者),自己处理降级

5.服务降级

1.一般服务降级放在消费端,即 消费者端 ,但是提供者端一样能使用。
首先提供者,即8001 先从自身找问题,设置自身调用超时的峰值,峰值内正常运行,超出峰值需要有兜底的方法处理,作服务降级fallback
2.在8001服务提供方引入Hystrix依赖

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

3.对 8001 的service进行配置(对容易超时的方法进行配置) :

@Service
public class PaymentService {

    /**
     * 可以正常访问的方法
     * @param id
     * @return
     */
    public String paymentInfo_Ok(Integer id){
        return "线程池:" + Thread.currentThread().getName() + "  ,paymentInfo_OK,id:" + id;
    }

    /**
     超时访问的方法
     */
    @HystrixCommand(fallbackMethod = "timeoutHandler",commandProperties = {
            //设置峰值,超过 3 秒,就会调用兜底方法,这个时间也可以由feign控制
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
    })
    public String paymentInfo_Timeout(Integer id){
        int interTime = 5;
        //int i = 10/0;
        try{
            TimeUnit.SECONDS.sleep(interTime);
        }catch (Exception e){
            e.printStackTrace();
        }
        return "线程池:" + Thread.currentThread().getName() + "  ,paymentInfo_Timeout,id:" + id + "耗时" + interTime + "秒钟";
    }
   // 定义服务出现异常之后,兜底的方法
    public String timeoutHandler(Integer id){
        return "服务异常,请重试......";
    }
}

4.在启动类上开启服务熔断

@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker //开启服务熔断
public class HystrixPaymentApplication {

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

启动测试
总结:
我们发现。只要是我们服务不可用了(调用超时、内部错误),都可以用降级来处理。

6.局部降级

1.将服务提供方关于所有服务降级的设置全部去掉
2.在服务消费方引入hsytrix依赖

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

3.在启动类上开启服务熔断

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients //开启Feign客户端
@EnableCircuitBreaker//开启熔断器
public class HystrixOrderApplication80 {

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

4.在controller编写降级逻辑

@RestController
@Slf4j
@SuppressWarnings("all")
public class OrderController {
    @Autowired
    OrderService orderService;
    @GetMapping("/consumer/payment/hystrix/{id}")
    public String paymentInfo_OK(@PathVariable("id")Integer id){
        log.info("paymentInfo_OKKKKOKKK");
        return orderService.paymentInfo_OK(id);
    }
    @HystrixCommand(fallbackMethod = "handeException", commandProperties = {
            //设置峰值,超过 1.5 秒,就会调用兜底方法
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value = "1500")
    })
    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    public String paymentInfo_Timeout(@PathVariable("id")Integer id){
        log.info("paymentInfo_timeout");
        return orderService.paymentInfo_Timeout(id);
    }

    public String handeException(Integer id){
        return "服务调用异常,请稍后再试.....";
    }
}

启动测试

7.全局服务降级

上面的降级策略,很明显造成了代码的杂乱,提升了耦合度,而且按照这样,每个方法都需要配置一个兜底方法,很繁琐。现在将降级处理方法(兜底方法)做一个全局的配置,设置共有的兜底方法和独享的兜底方法。
全局服务降级处理在服务消费方上面实现
1.在消费方的controller里面编写全局降级逻辑的方法

// 全局降级处理方法
public String globalHandler(){
    return "这是全局处理降级逻辑的方法.......";
}

2.使用注解开启全局服务降级逻辑处理
在这里插入图片描述
启动测试

8.全局服务降级—服务降级方法的抽取

1.在yml配置文件开启Feign基于对Hystrix的支持

# 用于服务降级 在注解@FeignClient 中添加 fallback 属性值
feign:
hystrix:
  enabled: true  # 在feign中开启 hystrix

2.定义一个类实现Feign客户端接口

@Component
public class FallBackService implements OrderService {
    @Override
    public String paymentInfo_OK(Integer id) {
        return "进行paymentInfo_OK方法降级处理......";
    }

    @Override
    public String paymentInfo_Timeout(Integer id) {
        return "进行paymentInfo_Timeout方法降级处理";
    }
}

3.修改Feign客户端接口

@FeignClient(value = "cloud-payment-service",fallback = FallBackService.class)
public interface OrderService {

    @GetMapping("/payment/hystrix/{id}")
    public String paymentInfo_OK(@PathVariable("id")Integer id);

    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfo_Timeout(@PathVariable("id")Integer id);
}

测试
这次我们模拟服务提供方挂掉了,将8001服务停止

拓展

熔断状态机3个状态:
1.Closed:关闭状态,所有请求都正常访问。
2.Open:打开状态,所有请求都会被降级。Hystix会对请求情况计数,当一定时间内失败请求百分比达到阈值,则触发熔断,断路器会完全打开。默认失败比例的阈值是50%,请求次数最少不低于20次。
3.Half Open:半开状态,open状态不是永久的,打开后会进入休眠时间(默认是5S)。随后断路器会自动进入半开状态。此时会释放部分请求通过,若这些请求都是健康的,则会完全关闭断路器,否则继续保持打开,再次进行休眠计时
实际上服务熔断 和 服务降级 没有任何关系,就像 java 和 javaScript
服务熔断,有点自我恢复的味道

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值