springcloud(Hystrix服务降级,服务熔断)

基础知识

首次分布式服务系统面临的问题
复杂的分布式体系结构中的应用程序有数十个依赖关系,每个依赖有时候难免发生问题,这个时候可能引发连锁反应,导致整个系统雪崩。
所以就有了Hystrix:
在这里插入图片描述
官网地址

Hystrix的主要作用:
服务降级:服务器忙,请稍候再试,不让客户端等待并立刻返回一个友好提示,fallback。发生的场景程序运行异常,超时,服务熔断触发服务降级,线程池/信号池打满也会导致服务降级。
服务熔断:类比保险丝,达到最大服务访问后直接拒绝访问,然后调用服务降级的方法。
服务限流:秒杀高并发操作,严禁一窝蜂的过来拥挤,大家排队,有序进行。

Hystrix使用

<dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
 </dependency>
  • 简单的引入案例
    我们首先写一个简单的应用进行压测,从而引出Hystrix
@Service
public class PamentService {
    public String info(Integer id) {
        return "线程池" + Thread.currentThread().getName() + "info_ok";
    }
    public String info_false(Integer id) {
        int time = 3;
        try {
            TimeUnit.SECONDS.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "线程池" + Thread.currentThread().getName() + "timeoutInfo 耗时 : " + time;
    }
}
@RestController
public class PaymentController {
    @Resource
    PamentService pamentService;
    @Value("${server.port}")
    private String port;

    @GetMapping("/pament/info/{id}")
    public String getInfo(@PathVariable("id") Integer id) {
        return pamentService.info(id);
    }
    @GetMapping("/pament/info_false/{id}")
    public String info_false(@PathVariable("id") Integer id) {
        return pamentService.info_false(id);
    }
}

接下来我们使用Jmeter进行压测
新建200个线程,每个线程循环100次
在这里插入图片描述
发送请求:
在这里插入图片描述
原本程序启动访问info的时候基本是0延迟,由于我们发送大量的请求到info_false,占用大量的资源造成拥堵,我们发现访问info的时候出现了卡顿,而接下来我们就要Hystrix来解决这个问题。

同时我们在消费端,去调用这两个服(用openfeign去调用提供端服务,可以参考springcloud服务接口调用OpenFeign这篇文章,这里不再重复),同样开启压测,结果当然一样也会拥堵。下面就来看看Hystrix的具体使用。

服务降级

我们在服务逻辑处配置fallback

@Service
public class PamentService {
    public String info(Integer id) {

        return "线程池" + Thread.currentThread().getName() + "info_ok";
    }
    @HystrixCommand(fallbackMethod = "timeOutHandler", commandProperties = {
            // 这个线程池的超时时间是3秒,超过3秒就出错
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
    })
    public String info_false(Integer id) {
        int time = 5;
        try {
            TimeUnit.SECONDS.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "线程池" + Thread.currentThread().getName() + "timeoutInfo 耗时 : " + time;
    }
    public String timeOutHandler(Integer id) {
        return "serveice bussy please wait";
    }

同时在主启动类添加注解:@EnableCircuitBreaker
运行程序,我们休眠5秒才返回结果。但是我们配置了服务3秒之后就超时,所以就会服务降级,结果如下:
在这里插入图片描述

注意:timeOutHandler这个方法相当于finally,用来兜底。它所接受的参数应该和服务方法的参数一致,否则会报错。比如这里我们给 info_false配置了服务降级,但是如果我们timeOutHandler不写参数就找不到fallback这个方法。

接下我们再测试一下,发生异常的情况:

public String info_false(Integer id) {
       int time = 5;
       int a = 100 / 0; // 人为的制造异常,用于测试异常发生,服务降级
       try {
           TimeUnit.SECONDS.sleep(time);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       return "线程池" + Thread.currentThread().getName() + "timeoutInfo 耗时 : " + time;
   }

毫无疑问它同样会降级
在这里插入图片描述
上面演示的是服务端的服务降级。

通常我们客户端可能也有一些限制,比如说3秒还没有返回结果就认为超时。这个时候需要对客户端进行配置。

因为我们是通过feign来调用,所以我们需要开启配置

feign:
  hystrix:
    enabled: true #如果处理自身的容错就开启。开启方式与生产端不一样。

@RestController
public class TestController {
    @Resource
    Servic servic;
    @GetMapping("consumer/getinfo/{id}")
    public String getInfo(@PathVariable("id") Integer id) {
        return  servic.getInfo(id);
    }
    @GetMapping("consumer/getfalse_info/{id}")
    public String getFalseInfo(@PathVariable("id") Integer id) {

        return  servic.info_false(id);
    }
    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    // 如果自己报错,也有兜底的方法
    @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500")  //3秒钟以内就是正常的业务逻辑
    })
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
        String result = servic.info_false(id);
        System.out.println("服务熔断了");
        return result;
    }

    //兜底方法
    public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id){
        return "我是消费者80,对付支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,(┬_┬)";
    }
}

主启动类:@EnableHystrix注解
这个是个复合注解:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@EnableCircuitBreaker
public @interface EnableHystrix {
}

我们提供服务的地方配置的是5s,这边超时配置的是1.5s很明显是超时,所以会发生服务降级。

按照上面的配置,我们每一个方法都要配置一个fallback,这样好像不太合理,有些fallback的逻辑是相同的。需要把它抽取出来,定义成为全局的fallback。

@RestController
@DefaultProperties(defaultFallback = "paymentTimeOutFallbackMethodGlobal")
public class TestController {
    @Resource
    Servic servic;
    // 添加这个注解,没有指定fallback那么就会直接使用默认的fallback
    @GetMapping("consumer/getinfo/{id}")
    @HystrixCommand
    public String getInfo(@PathVariable("id") Integer id) {
        System.out.println("jfdksdjfjdsjff");
        int a = 100/ 0;
        return  servic.getInfo(id);
    }
    @GetMapping("consumer/getfalse_info/{id}")
    public String getFalseInfo(@PathVariable("id") Integer id) {
        return  servic.info_false(id);
    }
    // 这边指定了fallback,那就用自己的fallback
    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500")  //3秒钟以内就是正常的业务逻辑
    })
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
        String result = servic.info_false(id);
        System.out.println("服务熔断了");
        return result;
    }
    //兜底方法
    public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id){
        return "我是消费者80,对付支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,(┬_┬)";
    }
    // 全局的兜底方法
    //兜底方法
    public String paymentTimeOutFallbackMethodGlobal(){
        return "我是消费者80,对付支付系统繁忙请10秒钟后再试或者自己运行出错请检查
        自己,(┬_┬)";
    }
}
```
特别注意:在上面我们配置的时候我提醒了参数一定要一致,但是在配置全局fallback的时候它是无参,因为我们并不知道那个方法发生了异常,所以我们不知道参数类型。

我们发现我们上面的业务逻辑和熔断是混合在一起的,那么我们怎么才能解耦呢?
```java
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT", fallback = ServiceImpl.class)
public interface Service {
    @GetMapping("/pament/info/{id}")
    String getInfo(@PathVariable("id") Integer id);
    @GetMapping("/pament/info_false/{id}")
    String info_false(@PathVariable("id") Integer id);

}
```
```java
@Component
public class ServiceImpl implements Service {
    @Override
    public String getInfo(Integer id) {
        return "服务异常!";
    }
    @Override
    public String info_false(Integer id) {
        return "服务异常!";
    }
}
```
这个时候比如我们把服务的提供方直接关掉,这个时候它是异常的,就会走实现类的fallback。
![在这里插入图片描述](https://img-blog.csdnimg.cn/f8035405eb3643fc92eaac9e80cbcbb8.png)
这样的话我们可以我们就实现了fallback与业务的解耦。
但是这里需要特别注意:这种方式是不会处理controller里面的异常的,如下面的代码它不会走服务降级的逻辑,这种方式专注的是某个服务的服务降级。
```java
 @Resource
    Service servic;
    @GetMapping("consumer/getinfo/{id}")
    public String getInfo(@PathVariable("id") Integer id) {
        int a = 100 / 0;
        return  servic.getInfo(id);
    }
```
#### **服务降熔断**
服务熔断是应对雪崩效应的一种服务链路保护机制。当某个服务出现问题会进行服务降级,等到此服务响应正常后,恢复调佣链路。
我们在服务的实现类上添加方法,配置这个服务熔断的相关配置。
```java
 // 服务熔断
    @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
            @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),  //是否开启断路器
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),   //请求次数
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),  //时间范围
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"), //失败率达到多少后跳闸
    })
    public String paymentCircuitBreaker(@PathVariable("id") Integer id){
        if (id < 0){
            throw new RuntimeException("*****id 不能负数");
        }
        String serialNumber = IdUtil.simpleUUID();

        return Thread.currentThread().getName()+"\t"+"调用成功,流水号:"+serialNumber;
    }
    // 服务降级fallback
    public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
        return "id 不能负数,请稍候再试,(┬_┬)/~~     id: " +id;
    }
```
controller 
```java
 @GetMapping("/pament/fuse/{id}")
  public String fuse(@PathVariable("id") Integer id) {
     return pamentService.paymentCircuitBreaker(id);
}
```
当我们携带负数ID进行请求的时候,就会不断的抛出异常,这个时候失败率超过60%。此时服务就会降级,这个时候我们正常的请求也会走fallback,过一定的时间后再请求会恢复正常。
![在这里插入图片描述](https://img-blog.csdnimg.cn/8b02147c970146798c4d84138f39b3ce.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/35a2cbcaba1341968574a1d041c17296.png)
更多相关的配置(以下图片都来自于尚硅谷):
![在这里插入图片描述](https://img-blog.csdnimg.cn/bb594475a34d45ee920d4e4ab87385ee.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/56b8c33deb4149ce8fe578929a66e538.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/0d527d3808994adb86fe555860a6b690.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/ae753ae528c14a4cb3138f42f97e57a4.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/72d12a398e0845fb8494e395734fc869.png)
### Hystrix图形界面(监控界面)
创建一个新的springboot项目,添加依赖:
```xml
<dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
 </dependency>
```
主启动类添加:@EnableHystrixDashboard
然后访问:http://localhost:9001/hystrix就可以看到如下界面
![在这里插入图片描述](https://img-blog.csdnimg.cn/33b4ef1aeca4403c8408ebbce0bed2f2.png)
在被监控的服务主启动类添加配置:
```java
@Bean
public ServletRegistrationBean getServlet(){
    HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
    ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
    registrationBean.setLoadOnStartup(1);
    registrationBean.addUrlMappings("/hystrix.stream");
    registrationBean.setName("HystrixMetricsStreamServlet");
    return registrationBean;
}
 
```
在界面填写监控地址:
http://localhost:8001/hystrix.stream
可以看到相关的信息:
![在这里插入图片描述](https://img-blog.csdnimg.cn/edfd194bc51a4ac68edc7474146770c2.png)

特别感谢:以上部分图片和代码来源于尚硅谷视频教学。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值