服务降级

        降级,通常指高峰期,为了保证核心服务正常运行,需要停掉一些不太重要的业务,或者某些服务不可用时,执行备用逻辑从故障服务中快速失败或快速返回,以保障主体业务不受影响。

支付模块

1,POM文件

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</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>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-test</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

2,YML文件

server:
  port: 8001

spring:
  application:
    name: cloud-payment-service

eureka:
  instance:
    instance-id: payment8001
    prefer-ip-address: true
    lease-expiration-duration-in-seconds: 90
    lease-renewal-interval-in-seconds: 30
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka

3,主启动类

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

4,业务类

@Service
public class PaymentService {
    public String paymentOK() {
        String s = UUID.randomUUID().toString();
        return Thread.currentThread().getName() + "payment OK" + "\t" + s;
    }


    @HystrixCommand(fallbackMethod = "paymentTimeoutFallback", commandProperties = {
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "5000")
    })
    public String paymentTimeout() {
        String s = UUID.randomUUID().toString();
        try {
            TimeUnit.SECONDS.sleep(6);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return Thread.currentThread().getName() + "payment timeout" + "\t" + s;
    }


    public String paymentTimeoutFallback() {
        return  "这是payment time out fallback 处理逻辑";
    }
}
@RestController
@Slf4j
public class PaymentController {

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

    @Autowired
    private PaymentService paymentService;

    @GetMapping("/payment/ok")
    public String paymentOK(){
        String result = paymentService.paymentOK();
        log.info(result);
        return "payment OK "+result;
    }

    @GetMapping("/payment/timeout")
    public String paymentTimeout(){
        String result = paymentService.paymentTimeout();
        log.info("payment timeout "+result);
        return "payment timeout"+"\t"+serverPort+"\t"+result;
    }
}

订单模块

1,POM文件

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</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>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-test</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

2,YML文件

server:
  port: 85
spring:
  application:
    name: cloud-hystrix-order-service
eureka:
  instance:
    instance-id: order85
    prefer-ip-address: true
    lease-renewal-interval-in-seconds: 30
    lease-expiration-duration-in-seconds: 90
  client:
    fetch-registry: true
    register-with-eureka: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka
feign:
  hystrix:
    enabled: true


ribbon:
  #指的是建立连接后从服务器读取到可用资源所用的时间
  ReadTimeout: 10000  #单位为毫秒
  #指的是建立连接所用的时间,适用于网络正常的情况下,两端连接所用的时间
  ConnectTimeOut: 5000  #单位为毫秒
  eureka:
    enabled: true

3,主启动类

@SpringBootApplication
@EnableEurekaClient
@EnableHystrix
@EnableFeignClients
public class HystrixOrder85Main {
    public static void main(String[] args) {
        SpringApplication.run(HystrixOrder85Main.class,args);
    }
}

4,业务类

@Component
public class OrderServiceImpl implements OrderService {
    @Override
    public String paymentOK() {
        return "consumer ok fallback ";
    }

    @Override
    public String paymentTimeout() {
        return "consumer timeout fallback";
    }
}
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE",fallback = OrderServiceImpl.class)
public interface OrderService {
    @GetMapping("/payment/ok")
    String paymentOK();

    @GetMapping("/payment/timeout")
    String paymentTimeout();
}
@RestController
public class OrderController {
    @Autowired
    private OrderService orderService;

    @GetMapping("/consumer/hystrix/payment/ok")
    public String paymentInfo_OK(){
        String s = orderService.paymentOK();
        return "consumer hystrix "+s;
    }

    @GetMapping("/consumer/hystrix/payment/timeout")
/*    @HystrixCommand(fallbackMethod = "fallback_handler",commandProperties = {
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "1000")
    })*/
    public String paymentInfo_timeout(){
        String s = orderService.paymentTimeout();
        return "consumer hystrix "+s;
    }

/*    public String fallback_handler(){
        return "consumer hystrix 85 fallback method";
    }*/
}

订单模块遇到异常情况,除了自身的fallback,还有支付模块的fallback。

服务熔断

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

在Spring Cloud框架里,熔断机制通过Hystrix实现,Hystrix会监控微服务间调用的状况。当失败的调用满足特定条件,如5秒内20次请求,10次失败,就会启动熔断机制,熔断机制的注解是@HystrixCommand。

 当Hystrix Command请求后端服务失败数量超过一定比例(默认50%),断路器会切换到开路状态(Open),这时所有请求会直接失败而不会发送到后端服务。断路器保持在开路状态一段时间(默认5秒),自动切换到半开路状态(HALF-OPEN)。这时会判断下一次请求的返回情况,如果请求成功,断路器切回闭路状态(CLOSED),否则重新切换到开路状态(OPEN)。Hystrix的断路器类似保险丝,一旦后端服务不可用,断路器会直接切断请求链,避免发送大量请求影响系统吞吐量,并且断路器有自我检测并恢复的能力。

断路器的状态:

  • open:打开状态。请求不再进行调用当前服务,内部设置时钟一般为MTTR(平均故障处理时间),当打开时长达到所设时钟则进入半熔断状态。
  • closed:关闭状态。服务正常调用。
  • half-open:半开状态,部分请求根据规则调用当前服务,如果请求成功且符合规则则认为当前服务恢复正常,关闭熔断。

涉及断路器的三个重要参数:

  1. 快照时间窗口:断路器确定是否打开需要统计一些请求和错误数据。而统计的时间范围就是快照时间窗口,默认为最近的10秒。
  2. 请求总数的阀值:在快照时间窗口内,必须满足请求总数阀值才有资格熔断。默认为20次,意味着在10秒内,如果该hystrix命令的调用次数不足20次,即使所有的请求都超时或其他原因失败,断路器都不会打开。
  3. 错误百分比阀值,当请求总数在快照时间窗口内超过了阀值,比如发生了30次调用,如果在这30次调用中,有15次发生了超时异常,也就是超过50%的错误百分比。在默认设定的50%阀值情况下,这时候就会将断路器打开。

断路器打开或关闭的条件:

  1. 当满足一定的阀值的时候(默认10秒内超过20个请求次数)。
  2. 当失败率达到一定比例的时候(默认10秒内超过50%的请求失败率)。
  3. 达到以上阀值,断路器将会开启。
  4. 当开启的时候,所有请求都不会进行转发。
  5. 一段时间之后(默认为5秒),这个时候断路器是半开状态,会让其中一个请求进行转发。如果成功,断路器会关闭,若失败,继续开启。重复4和5。

 支付模块

1,POM文件

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</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>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-test</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

2,YML文件

server:
  port: 8001

spring:
  application:
    name: cloud-payment-service

eureka:
  instance:
    instance-id: payment8001
    prefer-ip-address: true
    lease-expiration-duration-in-seconds: 90
    lease-renewal-interval-in-seconds: 30
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka

3,主启动类

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

4,业务类

@Service
public class PaymentService {
    public String paymentOK() {
        String s = UUID.randomUUID().toString();
        return Thread.currentThread().getName() + "payment OK" + "\t" + s;
    }


    @HystrixCommand(fallbackMethod = "paymentTimeoutFallback", commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")
    })
    public String paymentTimeout() {
        String s = UUID.randomUUID().toString();
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return Thread.currentThread().getName() + "payment timeout" + "\t" + s;
    }


    public String paymentTimeoutFallback() {
        return "这是payment time out fallback 处理逻辑";
    }

    //circuit breaker
    @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("参数小于0");
        }
        String randomId = UUID.randomUUID().toString();
        return "payment  "+"\t"+id + randomId;
    }

    public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id) {
        return "payment circuit breaker "+id;
    }
}
@RestController
@Slf4j
public class PaymentController {

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

    @Autowired
    private PaymentService paymentService;

    @GetMapping("/payment/ok")
    public String paymentOK(){
        String result = paymentService.paymentOK();
        log.info(result);
        return "payment OK "+result;
    }

    @GetMapping("/payment/timeout")
    public String paymentTimeout(){
        String result = paymentService.paymentTimeout();
        log.info("payment timeout "+result);
        return "payment timeout"+"\t"+serverPort+"\t"+result;
    }

    @GetMapping("/payment/circuitBreaker/{id}")
    public String paymentCircuitBreaker(@PathVariable("id") Integer id){
        return paymentService.paymentCircuitBreaker(id);
    }
}

当发送的请求,在一定时间内超过指定的阀值,且失败率超过指定值,这时断路器有closed状态切换到open状态。open状态下直接拒绝任何请求,在一段时间后自动切换到half-open状态,这时再发送请求,如果成功,则由half-open状态切换到closed状态;否则由half-open状态切换到open状态。

Hystrix Dashboard配置

除了隔离依赖服务的调用以外,Hystrix还提供了准实时的调用监控(Hystrix Dashboard),Hystrix会持续第记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功,多少失败等。Netflix通过hystrix-metrics-event-stream项目实现了对以上指标的监控。Spring Cloud也提供了Hystrix Dashboard的整合,对监控的内容转化成可视化界面。

1,创建项目模块

2,POM文件

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</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>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-test</artifactId>
        </dependency>
    </dependencies>

2,YML文件

server:
  port: 9001

3,主启动类,添加@EnableHystrixDashboard 表示开启仪表盘监控注解

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

4,微服务提供者添加监控jar

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

5,springboot默认路径为“/”,需要修改为“/hystrix.stream”。否则,会报错:Hystrix:Unable to connect to Command Metric Stream

HystrixMetricsStreamServlet源码给出通过xml配置

在springboot中可以采用@Bean方式配置。

解决方法(在微服务提供者配置):

配置bean

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

或者yml文件中加入

management:
  enabled: false
  endpoints:
    web:
      exposure:
        include: hystrix.stream
      base-path: /

6,测试

1)启动注册服务

2)启动监控服务

3)启动被监控的带Hystrix的微服务

4)输入地址访问http://localhost:8001/hystrix.stream

 

  •  Delay参数用来控制服务器上轮询监控信息的延迟时间,默认是2000毫秒,可以通过配置该属性来降低客户端的网络和cpu消耗。
  • Title参数对应了头部标题Hystrix Stream之后的内容,默认会使用具体监控实例的URL,可以通过配置该信息来展示更合适的标题。

当被监控的服务有被访问时,可以看出监控界面有变化。

 实心圆:共有两种含义。它通过颜色的变化代表了实例的健康程度,它的健康程度从绿色>黄色>橙色>红色递减;

该实心圆除了颜色的变化之外,它的大小也会根据实例请求流量发生变化,流量越大实心圆就越大,所以通过该实心圆的展示,就可以在大量实例中快速的发现故障实例和高压力实例。

 

 

 posted on 2021-01-05 22:20  会飞的金鱼  阅读(429)  评论(0)    收藏  举报