Hystrix service downgrade in SpringCloud

Introduction

Problems faced by distributed systems

Applications in complex distributed architectures have many dependencies, each of which will inevitably fail at some point. If the host application is not isolated from these external faults, it has the potential to be brought down by them.

For example, for an application that depends on 30 services, each with 99.99% uptime, you can expect the following:

99.9930 = 99.7% available

In other words, 0.03% of 100 million requests = 3000000 will fail

2 hours per month service is unavailable if everything works

reality is usually worse

When everything is fine, the request looks like this:

img

When one of the systems has latency, it can block the entire user request:

img

In the case of high traffic, the delay of a backend dependency may cause all resources on all servers to be saturated within a few seconds (PS: means that subsequent requests will not be immediately served

img

service avalanche

When multiple microservices are called, suppose microservice A calls microservice B and microservice C, and microservice B and microservice C call other microservices. This is the so-called "fan-out". If the call response time of a microservice on the fan-out link is too long or unavailable, the call to microservice A will occupy more and more system resources, causing the system to crash, the so-called "avalanche effect".

For high-traffic applications, a single backend dependency can saturate all resources on all servers within seconds. Worse than failing, these applications can also cause increased latency between services, straining backup queues, threads, and other system resources, leading to more cascading failures throughout the system. These all represent the need to isolate and manage failures and latencies so that the failure of a single dependency cannot bring down an entire application or system.

So usually when you find that an instance under a module fails, this module will still receive traffic at this time, and then this problematic module also calls other modules, which will cause cascading failures, or avalanches.

Hystrix

img

The official website github.com/Netflix/Hys… stop updating.

Hystrix is ​​an open source library for dealing with delay and fault tolerance of distributed systems. In distributed systems, many dependencies will inevitably fail calls, timeouts, exceptions, etc.Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,提高分布式系统的弹性

熔断机制是应对雪崩效应的一种微服务链路保户机制,当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的相应信息。当检测当该节点微服务调用响应正常后恢复调用链路,熔断机制的注解是@HystrixCommand

“熔断器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(保险丝),,某个异常条件被触发,直接熔断整个服务。,向调用方法返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出吊牌用方法无法处理的异常,就保证了服务调用方的线程不会被长时间占用,避免故障在分布式系统中蔓延,乃至雪崩

当你使用Hystrix来包装每个依赖项时,上图中所示的架构会发生变化,如下图所示:

每个依赖项相互隔离,当延迟发生时,它会被限制在资源中,并包含回退逻辑,该逻辑决定在依赖项中发生任何类型的故障时应作出何种响应

img

重要概念

服务降级:服务器忙,请稍候再试,不让客户端等待并立刻返回一个友好提示,fallback

哪些情况会触发降级

  • 程序运行异常
  • 超时
  • 服务熔断触发服务降级
  • 线程池/信号量打满也会导致服务降级

服务熔断:类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示

服务限流:秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行

环境代建

新建cloud-provider-hystrix-payment8001,7001服务注册中心方便启动测试成单机版

hystrix一般用于消费端,但是也可以用于服务端

image-20200708160619900

image-20200708160806255

pom

主要引入hystrix依赖,eureka client依赖

<dependencies>
    <!-- hystrix-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
    <dependency><!-- 引用自己定义的api通用包,可以使用Payment支付Entity -->
        <groupId>com.kylin</groupId>
        <artifactId>cloud-api-commons</artifactId>
        <version>1.0-SNAPSHOT</version>
    </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>
    <!--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-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>
</dependencies>

yml

image-20200708161547522

server:
  port: 8001

spring:
  application:
    name: cloud-provider-hystrix-payment

eureka:
  client:
    fetch-registry: true
    register-with-eureka: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka

主启动类

image-20200708161825314

@SpringBootApplication
@EnableEurekaClient
public class PaymentHystrixMain8001 {

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

service

创建PaymentService接口,编写两个方法一个能正常运行,一个会停止几秒在运行

image-20200708162045623

public interface PaymentService {

    String paymentInfo_OK(Integer id);

    String paymentInfo_TimeOut(Integer id);

}

编写PaymentServiceImpl实现类实现接口

image-20200708162237290

@Service
public class PaymentServiceImpl implements PaymentService {


    @Override
    public String paymentInfo_OK(Integer id) {

        return "线程池:" + Thread.currentThread().getName() + " paymentInfo_OK,id:" +id+ "\t" + "哈哈哈~";
    }

    @Override
    public String paymentInfo_TimeOut(Integer id) {

        int timeNumber = 3;
        try {
            //停止timeNumber秒
            TimeUnit.SECONDS.sleep(timeNumber);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "线程池:" + Thread.currentThread().getName() + " paymentInfo_TimeOut,id:" +id+ "\t" + "哈哈哈~"+"耗时(秒):"+timeNumber;
    }


}

controller

image-20200708162442597

@RestController
@Slf4j
public class PaymentController {

    @Resource
    PaymentService paymentService;

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

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

        String result = paymentService.paymentInfo_OK(id);
        log.info("****result:"+result);
        return result;
    }

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

        String result = paymentService.paymentInfo_TimeOut(id);
        log.info("****result:"+result);
        return result;
    }
}

测试

启动7001,8001进行测试

image-20200708163029666

访问http://localhost:8001/payment/hystrix/ok/31测试

image-20200708163056478

访问http://localhost:8001/payment/hystrix/timeout/31耗时至少3秒

image-20200708163134322

环境代建成功!

Jmeter压测测试

官网jmeter.apache.org/下载解压,bin目录下…

上述在非高并发情形下,还能勉强满足 我们使用开启Jmeter,来20000个并发压死8001,20000个请求都去访问paymentInfo_TimeOut服务

image-20200708163603052

保存

image-20200708163654201

添加HTTP请求

image-20200708163824687

配置请求路径

image-20200708163954123

点击运行

image-20200708164024611

image-20200708164053262

查看后台,正在有不断的正在进行访问测试

GIF

此时再次访问http://localhost:8001/payment/hystrix/ok/31可正常访问的请求。也会变得慢了起来,以前是毫秒就能完成,现在要2秒左右

image-20200708164254413

为什么会变慢:tomcat的默认的工作线程数被打满了,没有多余的线程来分解压力和处理。

上面还是服务提供者8001自己测试,假如此时外部的消费者80也来访问,那消费者只能干等,最终导致消费端80不满意,服务端8001直接被拖死

加入服务消费者测试

新建cloud-consumer-feign-hystrix-order80

image-20200708164848315

pom

<dependencies>
    <!--hystrix-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
    <dependency><!-- 引用自己定义的api通用包,可以使用Payment支付Entity -->
        <groupId>com.kylin</groupId>
        <artifactId>cloud-api-commons</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- openfeign -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <!--监控-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</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-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>
</dependencies>

yml

image-20200708165414714

server:
  port: 80


eureka:
  client:
    register-with-eureka: true    #表示不向注册中心注册自己
    fetch-registry: true   #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/

spring:
  application:
    name: cloud-provider-hystrix-order

主启动类

使用到了OpenFeign使用注解开启@EnableFeignClients,这里省略了@EnableEurekaClient

简而言之就是当我们引用了EurekaClient的依赖后,并且我们的两个开关不手动置为false,Spring就会自动帮助我们执行EurekaAutoServiceRegistration类里的start()方法,而注册的动作就是在该方法里完成的。所以,我们的EurekaClient工程,并不需要显式的在SpringBoot的启动类上标注@EnableEurekaClient注解,也可注册到注册中心。

image-20200708165608389

@SpringBootApplication
@EnableFeignClients
//@EnableEurekaClient//我们的两个开关不手动置为false,Spring就会自动帮助我们执行注册
public class OrderHystrixMain80 {
    public static void main(String[] args) {

        SpringApplication.run(OrderHystrixMain80.class,args);
    }
}

service

image-20200708170559955

@FeignClient("CLOUD-PROVIDER-HYSTRIX-PAYMENT")
public interface PaymentHystrixService {

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

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

}

controller

image-20200708170719733

@RestController
@Slf4j
public class OrderHystrixController {

    @Resource
    PaymentHystrixService paymentHystrixService;

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

        String result = paymentHystrixService.paymentInfo_OK(id);

        return result;
    }

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

        String result = paymentHystrixService.paymentInfo_TimeOut(id);
        return result;
    }

}

测试

启动80,8001,7001

image-20200708171005744image-20200708171018943

8001自测访问http://localhost:8001/payment/hystrix/ok/31

image-20200708171048778

http://localhost:8001/payment/hystrix/timeout/31

image-20200708171135220

一切正常

80端http://localhost/consumer/payment/hystrix/ok/80访问正常的速度也十分快

image-20200708171249929

Jmeter测试

image-20200708171349066

80要么出现报错

image-20200708171425679

要么就变得访问十分缓慢

image-20200708171517500

故障现象和导致原因

8001同一层次的其他接口服务被困死,因为tomcat线程里面的工作线程已经被挤占完毕。80此时调用8001,客户端访问响应缓慢,转圈圈

正因为有上述故障或不佳表现,才有我们的降级/容错/限流等技术诞生

解决

超时导致服务器变慢(转圈)-> 超时不再等待

出错(宕机或程序运行出错)-> 出错要有兜底

对方服务(8001)超时了,调用者(80)不能一直卡死等待,必须有服务降级

对方服务(8001)down机了,调用者(80)不能一直卡死等待,必须有服务降级

对方服务(8001)OK,调用者(80)自己出故障或有自我要求(自己的等待时间小于服务提供者),自己处理降级

服务降级

通过@HystrixCommand注解的fallbackMethod属性指定降级方法。groupKeycommandKey默认为方法名(当然threadPoolKey不指定时,默认和groupKey一致,所以也是方法名),也可以指定这三个key值,配置文件通过groupKey,commandKey,threadPoolKey使用恰当的配置。commandPropertiesthreadPoolProperties是通过@HystrixProperty的name value键值对进行配置。

服务提供者

设置自身调用超时时间的峰值,峰值内可以正常运行,超过了需要有兜底的方法处理,作服务降级fallback

service

如果再调用的方法上有形参,那么fallback方法也同样需要形参;不然会报 找不到fallback方法异常

image-20200708203808874

@Service
public class PaymentServiceImpl implements PaymentService {

    @Override
    public String paymentInfo_OK(Integer id) {

        return "线程池:" + Thread.currentThread().getName() + " paymentInfo_OK,id:" + id + "\t" + "哈哈哈~";
    }

    @Override
    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler", commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
    })
    public String paymentInfo_TimeOut(Integer id) {

        int timeNumber = 5;
        try {
            //停止timeNumber秒
            TimeUnit.SECONDS.sleep(timeNumber);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "线程池:" + Thread.currentThread().getName() + " paymentInfo_TimeOut,id:" + id + "\t" + "哈哈哈~" + "耗时(秒):" + timeNumber;
    }


    public String paymentInfo_TimeOutHandler(Integer id) {
        return "线程池:" + Thread.currentThread().getName() + " 系统繁忙或者运行报错,请稍后再试,id:" + "\t" + "/(ㄒoㄒ)/~~";
    }


}

主启动类

在启动类上加上@EnableCircuitBreaker,开启断路器功能

image-20200708204143260

@SpringBootApplication
@EnableEurekaClient
//开启断路器功能
@EnableCircuitBreaker
public class PaymentHystrixMain8001 {

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

测试

启动7001,80,访问http://localhost:8001/payment/hystrix/timeout/31

image-20200708204444395

用来单独的线程池来处理,当等待时间超过了3秒调用fallbackMethod

image-20200708204707208

这个异常属于超时异常能被处理,我们修改异常为其他异常看看能不能被处理

image-20200708205142401

@Service
public class PaymentServiceImpl implements PaymentService {

    @Override
    public String paymentInfo_OK(Integer id) {

        return "线程池:" + Thread.currentThread().getName() + " paymentInfo_OK,id:" + id + "\t" + "哈哈哈~";
    }

    @Override
    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler", commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
    })
    public String paymentInfo_TimeOut(Integer id) {

        int num = 10/0;
/*        int timeNumber = 5;
        try {
            //停止timeNumber秒
            TimeUnit.SECONDS.sleep(timeNumber);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }*/
        //return "线程池:" + Thread.currentThread().getName() + " paymentInfo_TimeOut,id:" + id + "\t" + "哈哈哈~" + "耗时(秒):" + timeNumber;
        return "线程池:" + Thread.currentThread().getName() + " paymentInfo_TimeOut,id:" + id + "\t" + "哈哈哈~";
    }


    public String paymentInfo_TimeOutHandler(Integer id) {
        return "线程池:" + Thread.currentThread().getName() + " 系统繁忙或者运行报错,请稍后再试,id:" + "\t" + "/(ㄒoㄒ)/~~";
    }


}

重启测试访问http://localhost:8001/payment/hystrix/timeout/31

image-20200708205316524

同样出现异常使用fallbackMethod,一进入请求中发生报错

结论:只要当前服务不可用,就会进行服务降级,调用fallbackMethod

服务消费者

进行客户端降级保护,一般服务降级用在客户端

yml

修改80客服端的配置文件,当前我们使用的是Feign做服务调用,开启使用Hystrixfeign.hystrix.enabled=true

image-20200708210252504

server:
  port: 80

spring:
  application:
    name: cloud-provider-hystrix-order

eureka:
  client:
    register-with-eureka: true    #表示不向注册中心注册自己
    fetch-registry: true   #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/



feign:
  hystrix:
    enabled: true #feign开启hystrix

主启动类

使用@EnableHystrix开启Hystrix

image-20200708210438164

@SpringBootApplication
@EnableFeignClients
//@EnableEurekaClient//我们的两个开关不手动置为false,Spring就会自动帮助我们执行注册
@EnableHystrix//开启Hystrix
public class OrderHystrixMain80 {
    public static void main(String[] args) {

        SpringApplication.run(OrderHystrixMain80.class,args);
    }
}

controller

如果再调用的方法上有形参,那么fallback方法也同样需要形参;不然会报 找不到fallback方法异常

同样使用@HystrixCommand注解

image-20200708210656501

当调用时间超过了1.5秒,运行fallbackMethod方法

@RestController
@Slf4j
public class OrderHystrixController {

    @Resource
    PaymentHystrixService paymentHystrixService;

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

        String result = paymentHystrixService.paymentInfo_OK(id);

        return result;
    }

    @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 = paymentHystrixService.paymentInfo_TimeOut(id);
        return result;
    }

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

}

测试

启动80 8001 7001http://localhost/consumer/payment/hystrix/timeout/31

出现报错

image-20200708211218085

image-20200708212635227

但是时间确实1秒多钟就进行了服务降级,而不是想象中的1.5秒多出现超时报错。这是为什么了?

原来是Hystrix的配置问题,hystrix.command.default.execution.timeout.enabled ,为false则超时控制有ribbon控制,为true则hystrix超时和ribbon超时都是用,但是谁小谁生效,默认为true。Hystrix的hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds超时时长默认1秒。ribbon的默认超时控制也为1秒,所以现在整个服务的超时时间都为一秒钟

我们修改配置文件,将ribbon和hystrix的超时控制时间修改为10秒

image-20200708212135605

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 100000 #hystrix超时控制时长
#
##设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
  #指的是建立连接后从服务器读取到可用资源所用的时间
  ReadTimeout: 10000
  #指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
  ConnectTimeout: 10000

重启80,再次访问http://localhost/consumer/payment/hystrix/timeout/31

image-20200708212311097

同样出现了超时报错,进入了fallbackMethod方法。等待时长也是和我们期望的一样至少1.5秒。

为了进一步测试,我们将80的可以等待的超时时长改为10秒

image-20200708212848866

重新启动80,访问http://localhost/consumer/payment/hystrix/timeout/31,成功调用了8001提供的服务,等待时间也至少大于3秒。!

image-20200708212941097

测试成功,但也存在问题,每个业务方法对应一个兜底的方法,代码膨胀,如果每一个方法都要对应一个配置,代码量爆炸。缺少全局的配置

全局服务降级

使用@DefaultProperties注解,配置defaultFallback属性,值为方法名。使用了@HystrixCommand没有配置fallback的就是用全局的,配置了就使用自己的。就近原则

image-20200708214258686

@RestController
@Slf4j
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")
public class OrderHystrixController {

    @Resource
    PaymentHystrixService paymentHystrixService;

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

        String result = paymentHystrixService.paymentInfo_OK(id);

        return result;
    }

    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
/*    @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "10000")
    })*/
    @HystrixCommand
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
        int i = 10/0;
        String result = paymentHystrixService.paymentInfo_TimeOut(id);
        return result;
    }

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


    //全局fallback
    public String payment_Global_FallbackMethod(){
        return "Global异常处理信息,请稍后再试,/(ㄒoㄒ)/~~";
    }


}

测试

重启80,访问http://localhost/consumer/payment/hystrix/timeout/31

全局服务降级配置成功!解决了代码膨胀的问题

image-20200708214407154

通配服务降级

上文我们的fallback方法和业务逻辑混一起,是有点混乱的。

根据cloud-consumer-feign-hystrix-order80已经有的PaymentHystrixService接口,重新新建一个类PaymentFallbackService实现该接口,统一为接口里面的方法进行异常处理。当这个Service的方法出现了异常,而请求中没有配置@HystrixCommand则使用我们这个类中的异常处理方法。

新建PaymentFallbackService类实现PaymentHystrixService,重写方法里是对该方法出现异常的操作。

image-20200708220523482

@Service
public class PaymentFallbackService implements PaymentHystrixService {

    @Override
    public String paymentInfo_OK(Integer id) {
        return "-----PaymentFallbackService fall back-paymentInfo_OK , (┬_┬)";
    }

    @Override
    public String paymentInfo_TimeOut(Integer id) {
        return "-----PaymentFallbackService fall back-paymentInfo_TimeOut , (┬_┬)";
    }
}

PaymentHystrixService中的@FeignClient配置fallback属性,值为PaymentFallbackService.class

image-20200708220818691

@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class)
public interface PaymentHystrixService {

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

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

}

测试

重新启动80测试,访问http://localhost/consumer/payment/hystrix/ok/31因为改请求没有配置@HystrixCommand,所以当Service访问服务出现异常时,则会掉用fallback类中的对应方法

image-20200708220902296

目前一切正常

image-20200708221055195

当我们把8001服务给关闭后,Service访问服务出现异常时,则会掉用fallback类中的对应方法。

image-20200708221142914

再次访问,服务降级配置成功!

image-20200708221202492

服务熔断

熔断机制是应对雪崩效应的一种微服务链路保护机制,

当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回”错误”的响应信息。当检测到该节点微服务响应正常后恢复调用链路,在SpringCloud框架机制通过Hystrix实现,Hystrix会监控微服务见调用的状况,当失败的调用到一个阈值,缺省是5秒内20次调用失败就会启动熔断机制,熔断机制的注解是@HystrixCommand

修改cloud-provider-hystrix-payment8001

Servcie

image-20200709091848586

public interface PaymentService {

    String paymentInfo_OK(Integer id);

    String paymentInfo_TimeOut(Integer id);

    String paymentCircuitBreaker(@PathVariable("id") Integer id);
}

image-20200709092004082

//服务熔断
@Override
@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;
}

public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
    return "id 不能负数,请稍候再试,(┬_┬)/~~     id: " +id;
}

circuitBreaker.enabled:是否启用熔断器,默认为 true;

circuitBreaker.forceOpencircuitBreaker.forceClosed:是否强制启用/关闭熔断器,强制启用关闭都想不到什么应用的场景,保持默认值,不配置即可。

circuitBreaker.requestVolumeThreshold:启用熔断器功能窗口时间内的最小请求数。试想如果没有这么一个限制,我们配置了 50% 的请求失败会打开熔断器,窗口时间内只有 3 条请求,恰巧两条都失败了,那么熔断器就被打开了,5s 内的请求都被快速失败。此配置项的值需要根据接口的 QPS 进行计算,值太小会有误打开熔断器的可能,值太大超出了时间窗口内的总请求数,则熔断永远也不会被触发。建议设置为 QPS * 窗口秒数 * 60%

circuitBreaker.errorThresholdPercentage:在通过滑动窗口获取到当前时间段内 Hystrix 方法执行的失败率后,就需要根据此配置来判断是否要将熔断器打开了。 此配置项默认值是 50,即窗口时间内超过 50% 的请求失败后会打开熔断器将后续请求快速失败。

circuitBreaker.sleepWindowInMilliseconds:熔断器打开后,所有的请求都会快速失败,但何时服务恢复正常就是下一个要面对的问题。熔断器打开时,Hystrix 会在经过一段时间后就放行一条请求,如果这条请求执行成功了,说明此时服务很可能已经恢复了正常,那么会将熔断器关闭,如果此请求执行失败,则认为服务依然不可用,熔断器继续保持打开状态。此配置项指定了熔断器打开后经过多长时间允许一次请求尝试执行,默认值是 5000。

controller

image-20200709092205633

//===服务熔断
@GetMapping("/payment/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
    String result = paymentService.paymentCircuitBreaker(id);
    log.info("*******result:"+result);
    return result;
}

测试

启动8001 7001http://localhost:8001/payment/circuit/31

image-20200709092808186

访问http://localhost:8001/payment/circuit/-31

image-20200709092838761

After multiple visits to negative ids, the service will be broken. At this time, accessing positive ids will also call fallbackmethod

After a period of time, send a correct request with a positive id, and the service will be shut down

image-20200709093025231

work process

Official website github.com/Netflix/Hys…

img

image-20200709093232873

Service monitoring HystrixDashboard

Hystrix also provides real-time call monitoring (Hystrix Dashboard). Hystrix will continuously record the execution information of all requests initiated through Hystrix, and display them to users in the form of statistical reports and graphics, including how many requests are executed per second and how many are successful. How many failures etc.

Netflix has implemented the monitoring of the above indicators through the hystrix-metrics-event-stream project. Spring Cloud also provides the integration of Hystrix Dashboard, which converts monitoring content into a visual interface.

to build

new buildcloud-consumer-hystrix-dashboard9001

image-20200709093556464

pom

<dependencies>
    <!--新增hystrix dashboard-->
    <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>

    <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>
</dependencies>

yml

Just configure the port number to 9001

server:
  port: 9001
 

main startup class

Use @EnableHystrixDashboardto open the Hystrix graphical interface

image-20200709094022219

monitoring configuration

If you want to monitor the provided classes of microservices, you need to import the monitoring dependency configuration, which we have configured before

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

test

Start 9001, visit http://localhost:9001/hystrix, build successfully

image-20200709094506892

combat

Start cloud-consumer-hystrix-dashboard9001the microservice and then monitor the microservice 8001

Revisecloud-provider-hystrix-payment8001

main startup class

image-20200709094826728

@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;
}

The new version of Hystrix needs to specify the monitoring path in the main startup class MainAppHystrix8001, otherwise an error Unable to connect to Command Metric Stream will be reported

test

Start 8001 and fill in the data on the monitoring interfacehttp://localhost:8001/hystrix.stream

image-20200709095415004

Click the button to monitor 8001

image-20200709095623432

Access errors and correct tests separately

GIF958

image-20200709100133863

image-20200709100142509

Guess you like

Origin juejin.im/post/7240371866287226939