springcloud-一篇搞定Hystrix

Hystrix是什么?

基础概念

服务雪崩

在分布式微服务的系统中,一定是会存在某个A服务调用多个服务,而多个服务也会继续去掉其他服务。因此服务的调用,从调用的结构上看是“扇形”趋势。
因此就会存在一个很重大的问题:如果其中一个服务出现问题了咋办,比如超时,宕机,异常....就会导致前面的调用方在死等着,占据着资源。这还只是一个请求情况下,如果是在高并发的情况下,由于多个服务滞留占用资源,发生蝴蝶效应,导致系统整体服务使用出现缓慢甚至不可以,叫服务雪崩。
这个影响主要有两个:

  1. 假设A服务出现异常,调用A服务所在的服务链路将不能完成整体业务功能
  2. 由于A服务出现异常,那些滞留占用资源的服务影响到了其他正常服务的使用。

Hystrix是为了解决第二个问题而出现的延迟和容错的开源库。第一种影响解决不了,本身A服务有问题,所在那条链路肯定完成不了整体功能了,但起码不能影响其他服务的使用吧

服务降级

服务降级:就是说用户发送请求到A服务接口,由于A服务出现不明故障导致不能及时用户返回正确的结果,这时启动备用方案;
一般来说备用方案,通常是返回一个友好的提示给用户,并不能真正完成用户要做的业务
导致服务降级的几种情况:

  1. 程序运行异常
  2. 超时
  3. 服务熔断调用服务降级
  4. 线程池打满

总结:服务降级是一种备用方案。

服务熔断

服务熔断:就是说用户发送请求到A服务接口,由于A服务出现不明重大故障导致不能给用户返回正确的结果,直接把A服务熔断掉,直接拒绝用户访问A服务了
这里也说下服务熔断和服务降级的区别:

出现服务降级的情况,可能这时的A服务还是正常的,只不过当前压力比较大,大部分请求能及时响应,部分请求无法及时响应,因此给部分无法及时响应给出备用方案
而服务熔断,是A服务受到了极大的影响,整个服务不可用了,这时直接拒绝用户访问A服务是熔断操作

某个服务发生了服务熔断后,后续一般会先 先调用服务降级,给用户一个友好提示,接着恢复服务的使用。

服务限流

服务限流理解就比较简单点,A服务1秒钟只能处理10个请求,现在同一时间有100个请求调用A服务,A服务肯定承受不了,因此服务限流可以理解为将这100个请求进行排队,不要挤在一块访问A服务

服务降级

Hystrix使用方式1:在controller接口加上@HystrixCommand,并在controller类加上备案方法

降级在生产者服务端

  1. 在主启动类加上@EnableHystrix
  2. 给可能会出现高并发或超时的接口加上@HystrixCommand,并编写备案方法,如下:
    @GetMapping("/payment/hystrix/timeout/{id}")
    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="3000")
    })
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id) throws InterruptedException
    {
        String result = paymentService.paymentInfo_TimeOut(id);
        log.info("****result: "+result);
        return result;
    }

    public String paymentInfo_TimeOutHandler(Integer id){
        return "/(ㄒoㄒ)/调用支付接口超时或异常:\t"+ "\t当前线程池名字" + Thread.currentThread().getName();
    }

如果调用paymentInfo_TimeOut方法超过3秒或者异常,则会调用paymentInfo_TimeOutHandler方法进行响应。

降级在消费者服务端

  1. yml加上如下,因为消费者服务使用feign作为服务调用,和hystrix有紧密合作:
feign:
  hystrix:
    enabled: true
  1. 在主启动类加上@EnableHystrix
  2. 示例代码:
    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = {
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1500")
    })
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id)
    {
        String result = paymentHystrixApi.paymentInfo_TimeOut(id);
        return result;
    }
    public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id)
    {
        return "我是消费者80,对方支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,o(╥﹏╥)o";
    }

总结

上述的写法有两个弊病:

  1. 备案方法和controller耦合在一起了
  2. 每个需要降级的方法都得对应一个备案方法,代码冗余

Hystrix使用方式2:在controller接口加上@DefaultProperties,在controller需要降级的方法加上@HystrixCommand

使用方式1的弊病之一:代码会出现冗余。而使用方式2是一定程度上减少了代码冗余,具体使用步骤如下(激活注解和yml步骤省略):

  1. 在controller类上加上@DefaultPropertie,如下:
@RestController
@Slf4j
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod",commandProperties = {
        @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1500")
})
public class OrderHystirxController
{}
  1. 在@DefaultProperties所在类,需要降级的方法加上@HystrixCommand,如下:
    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    @HystrixCommand
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id)
    {
        String result = paymentHystrixApi.paymentInfo_TimeOut(id);
        return result;
    }

总结

优点:

  1. 一定程度上减少了降级方法冗余
  2. 类里的降级方法,@HystrixCommand如果没有指定具体的降级方法和超时时间,则走默认的@DefaultProperties。如果指定了,则走自己的。

缺点:

  1. 备案方法和controller耦合在一起了

Hystrix使用方式3:创建一个备案类实现ClientApi接口,重写的方法即为备案方法

话不多说,操作如下(激活注解和yml省略):

  1. 创建PaymentHystrixApiFallback实现 PaymentHystrixApi,重写的方法即为备案方法:
@Component
public class PaymentHystrixApiFallback implements PaymentHystrixApi {

    @Override
    public String paymentInfo_OK(@PathVariable("id") Integer id){
        return "服务调用失败,提示来自:cloud-consumer-feign-order80";
    }

    @Override
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
        return "服务调用失败,提示来自:cloud-consumer-feign-order80";
    }
}
  1. 在指定备案类PaymentHystrixApiFallback,如下:
@Component
@FeignClient(value = "cloud-provider-hystrix-payment",fallback = PaymentHystrixApiFallback.class)
public interface PaymentHystrixApi {
    @GetMapping("/payment/hystrix/ok/{id}")
    String paymentInfo_OK(@PathVariable("id") Integer id);

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

假设我们调用paymentInfo_OK方法失败了,就会调用其重写方法返回"服务调用失败,提示来自:cloud-consumer-feign-order80";

总结

优点:

  1. 将降级方法抽取出来,实现解耦。
    2.从根本上解决问题,此方式偏向考虑服务之间调用失败而做出的降级反馈,而方式1和2有很大的不同,方式1和2是针对自身方法,而方式3针对的是服务调用方法

缺点:

  1. 每个服务接口都有一个备案方法,代码冗余

方式3使用的会比较多一点,但具体情况依项目而定;服务降级的使用多用在消费者服务端

服务熔断

Hystrix服务熔断有3个状态,open,close,half-open

open: 熔断已开启
close:熔断关闭
half-open:熔断半开启

如果服务在正常情况下,一般是close状态,如果由于某种原因出现熔断了,会进行open状态,在休眠一段时间后会进入half-open状态,尝试处理请求,如果处理请求还是失败的,又会回到open状态,反之close。
代码示例实际上和服务降级差不多,但不一样是服务熔断只针对自身方法,如下:

    //=========服务熔断
    @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
	//是否开启熔断机制
            @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),
			//某段时间内请求次数阈值
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),
			//进入open状态,等待多久才进入到half-open状态
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),
			//达到多少失败百分比进入open状态
            @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;
    }
    public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id)
    {
        return "id 不能负数,请稍后再试,/(ㄒoㄒ)/~~   id: " +id;
    }

上面的一些参数可举个例子:最近10次如果失败率达到60%以上,则进入open状态,后面的请求(不管对错)直接返回备案方法。在休眠10秒后进入half-open状态尝试处理请求,如果请求能正常处理,则进入close状态,反之open。

posted @ 2022-04-01 17:01  爱编程DE文兄  阅读(63)  评论(0编辑  收藏  举报