springcloud服务单例容错hystrix(十)

一、概述

1.1、为什么需要服务容错

  • 雪崩效应:如下图服务a出错可能会导致服务b,c相继出现通信错误,继而引发服务间调用的级联故障;而Hystrix后备可防止级联故障,开路可停止级联故障,并让不堪重负的服务时间得以恢复。后备可以是另一个受Hystrix保护的呼叫,静态数据或合理的空值。可以将回退链接在一起,以便第一个回退进行其他业务调用,然后回退到静态数据。
  • Hystrix作用:服务熔断,服务降级,依赖隔离,监控
    在这里插入图片描述

1.2、服务降级:简单示例

  • 以a-mall项目的订单服务调用商品服务的服务间通信为例:
  • springcloud版本:Greenwich.SR3
  • springboot版本:2.1.8.RELEASE
1、引入Hystrix依赖包
<!--引入hytrix依赖包-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
2、启动类添加@EnableCircuitBreaker标签
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@MapperScan("com.mall.order.server.dao.mapper")
@EnableCircuitBreaker //开启熔断
public class OrderServerApplication {

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

}

3、在需要熔断的地方添加@HystrixCommand
  • 如下所示:在order服务中使用RestTemplate 动态调用product服务中的接口;@HystrixCommand标签中fallbackMethod 的value值就是调用product服务接口失败时返回的接口方法名;这一过程叫做服务降级
    package com.mall.order.server.controller.test;
    
    import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.client.ServiceInstance;
    import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    import java.util.Arrays;
    
    /**
     * 熔断器测试controller
     */
    @RestController
    @RequestMapping("/hystrix/test")
    public class HystrixTestController {
    
        @Autowired
        LoadBalancerClient loadBalancerClient;
    
        @HystrixCommand(fallbackMethod = "fallback")
        @GetMapping("/getProductList")
        public String getProductList(){
            RestTemplate restTemplate = new RestTemplate();
            ServiceInstance serviceInstance = loadBalancerClient.choose("PRODUCT");
            String result = restTemplate.postForObject(
                    String.format("http://%s:%s",serviceInstance.getHost(),serviceInstance.getPort())
                            + "/productClient/providerList",
                    Arrays.asList("984325","894327"),
                    String.class);
            return result;
        }
    
        public String fallback(){
            return "访问时间过长,请稍后再试";
        }
    }
    
    
4、启动order服务,但是关闭product服务
  • 调用3中的接口,此时product已经不能提供接口通信,触发hystrix服务降级机制:

在这里插入图片描述

5、给order自己的服务降级
  • 如下所示:不是product服务调用失败导致了服务降级,而是order服务自己的异常也触发了服务降级;所以hystrix也是进行本地服务降级的不二之选。
package com.mall.order.server.controller.test;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.Arrays;

/**
 * 熔断器测试controller
 */
@RestController
@RequestMapping("/hystrix/test")
public class HystrixTestController {

    @Autowired
    LoadBalancerClient loadBalancerClient;

    @HystrixCommand(fallbackMethod = "fallback")
    @GetMapping("/getProductList")
    public String getProductList(){
//        RestTemplate restTemplate = new RestTemplate();
//        ServiceInstance serviceInstance = loadBalancerClient.choose("PRODUCT");
//        String result = restTemplate.postForObject(
//                String.format("http://%s:%s",serviceInstance.getHost(),serviceInstance.getPort())
//                        + "/productClient/providerList",
//                Arrays.asList("984325","894327"),
//                String.class);
//        return result;
        throw new RuntimeException("error");
    }

    public String fallback(){
        return "访问时间过长,请稍后再试";
    }
}

1.3、服务降级:全局默认配置

1、controller上面设置一个全局的defaultFallBack
  • 使用标签**@DefaultProperties**进行hystrix全局配置
@DefaultProperties(defaultFallback = "defaultFallback")
2、全局配置代码
  • 如果没有声明此方法服务降级回调方法,默认调用**@DefaultProperties**标签中的方法
package com.mall.order.server.controller.test;

import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.Arrays;

/**
 * 熔断器测试controller
 */
@RestController
@RequestMapping("/hystrix/test")
@DefaultProperties(defaultFallback = "defaultFallback")
public class HystrixTestController {

    @Autowired
    LoadBalancerClient loadBalancerClient;

    //如果没有声明此方法服务降级回调方法,默认调用@DefaultProperties标签中的方法
    @HystrixCommand
    @GetMapping("/getProductList")
    public String getProductList(){
        RestTemplate restTemplate = new RestTemplate();
        ServiceInstance serviceInstance = loadBalancerClient.choose("PRODUCT");
        String result = restTemplate.postForObject(
                String.format("http://%s:%s",serviceInstance.getHost(),serviceInstance.getPort())
                        + "/productClient/providerList",
                Arrays.asList("984325","894327"),
                String.class);
        return result;
//        throw new RuntimeException("error");
    }

    private String fallback(){
        return "访问时间过长,请稍后再试";
    }

    private String defaultFallback(){
        return "默认服务降级:访问时间过长,请稍后再试";
    }
}

  • 测试结果:调用了defaultFallback方法
    在这里插入图片描述

1.4、超时设置

1、将product服务提供服务的接口方法设置一个两秒的线程,然后启动product服务
@RestController
	@RequestMapping("/productClient")
	public class ProductClientController {
	 @PostMapping("/providerList")
	    public List<ProductInfoVo> providerProductList(@RequestBody List<String> productIds) {
	        //调用时给一个2s的线程睡眠时间
	        try {
	            Thread.sleep(2000);
	        } catch (InterruptedException e) {
	            e.printStackTrace();
	        }
	        return productService.getListByProductIds(productIds);
	    }
	    }
2、在order端调用通信接口
  • 可以观察到,如果超过一秒未拿到响应数据就会触发hystrix的服务降级
    在这里插入图片描述
3、hystrix默认超时时间
  • 打开HystrixCommandProperties类,发现default_executionTimeoutInMilliseconds(默认超时时间,ms)设置为了1s,所以上述在1.03s时触发了服务降级。
    在这里插入图片描述
4、找到default_executionTimeoutInMilliseconds对应的配置名字
  • 还是在HystrixCommandProperties类中ctr + f查找default_executionTimeoutInMilliseconds的配置名字

在这里插入图片描述

5、@HystrixCommand标签中自定义超时时间3s
  @HystrixCommand(commandProperties = {
           @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
    })
    @GetMapping("/getProductList")
    public String getProductList(){
        RestTemplate restTemplate = new RestTemplate();
        ServiceInstance serviceInstance = loadBalancerClient.choose("PRODUCT");
        String result = restTemplate.postForObject(
                String.format("http://%s:%s",serviceInstance.getHost(),serviceInstance.getPort())
                        + "/productClient/providerList",
                Arrays.asList("984325","894327"),
                String.class);
        return result;
//        throw new RuntimeException("error");
    }
6、重启order服务测试
  • 请求成功:控制台中我们可以看到本次请求用了2.04s,但是因为我们设置的hystrix超时时间为3秒,所以就可以正常访问,不会触发服务降级
    在这里插入图片描述

二、服务熔断

1.1、简单示例

1、服务熔断四大基本配置项
  • circuitBreaker.enabled:是否使用断路器,默认为true
  • circuitBreaker.requestVolumeThreshold:滑动窗口的大小,默认为20
  • circuitBreaker.sleepWindowInMilliseconds:过多长时间,熔断器再次检测是否开启,默认为5000,即5s钟
  • circuitBreaker.errorThresholdPercentage:错误率,默认50%
package com.mall.order.server.controller.test;

import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.sun.net.httpserver.Authenticator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.Arrays;

/**
 * 熔断器测试controller
 */
@RestController
@RequestMapping("/hystrix/test")
@DefaultProperties(defaultFallback = "defaultFallback")
public class HystrixTestController {

    @Autowired
    LoadBalancerClient loadBalancerClient;

    @HystrixCommand(commandProperties = {
           @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1000"),
            @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60")
    })
    @GetMapping("/getProductList")
    public String getProductList(@RequestParam("number") int number){
        if(number % 2 == 0){
              return "success";
        }
        
        RestTemplate restTemplate = new RestTemplate();
        ServiceInstance serviceInstance = loadBalancerClient.choose("PRODUCT");
        String result = restTemplate.postForObject(
                String.format("http://%s:%s",serviceInstance.getHost(),serviceInstance.getPort())
                        + "/productClient/providerList",
                Arrays.asList("984325","894327"),
                String.class);
        return result;
//        throw new RuntimeException("error");
    }

    private String fallback(){
        return "访问时间过长,请稍后再试";
    }

    private String defaultFallback(){
        return "默认服务降级:访问时间过长,请稍后再试";
    }
}

2、测试用例
  • 如1中代码所示
    1、当number值为奇数时,调用restTemplate逻辑代码;但是因为现在超时时间是1秒,而product服务那边设置了2s的线程等待,所以restTemplate代码肯定会触发服务降级;
    2、当number为偶数时,直接返回一个success字符串,代表请求成功没有触发服务降级
  • 结果:number = 1请求次数过多时,number = 2也不能正常访问了,触发了服务降级
    在这里插入图片描述
    在这里插入图片描述

1.2、执行流程

1、1.1中的断路器执行流程(以代码中的配置为基础)

步骤一:当number为1时的请求次数较多,在10次请求中占比超过60%时触发断路器,断路器打开,整个接口处于断路状态;
步骤二:因为整个接口断路,number=2的正常请求不能请求了,只能触发服务降级
步骤三:10秒过后断路器模式开始重新检测,如果10次以内请求异常百分比没超过60%时,断路器关闭,number = 2请求可以正常访问
在这里插入图片描述

2、hystrix原理图

在这里插入图片描述

  • Closed:熔断器关闭状态(所有请求返回成功)

  • Open:熔断器打开状态(调用次数累计到达阈值或者比例,熔断器打开,服务直接返回错误)

  • Half Open:熔断器半开状态(默认时间过后,进入半熔断状态,允许定量服务请求,如果调用都成功,则认为恢复了,则关闭断路器,反之打开断路器)

1.3、application.yml中配置hystrix

1、default默认配置
hystrix:
  command:
    default:
      # 断路器相关配置
      circuitBreaker:
        # 是否开启断路器
        enabled: true
        # 滑动窗口的大小,默认为20
        requestVolumeThreshold: 10
        # 过多长时间,熔断器再次检测是否开启,默认为5000,即5s钟
        sleepWindowInMilliseconds: 10000
        # 错误率,默认50%
        errorThresholdPercentage: 60
      execution:
        isolation:
          thread:
            # 配置超时时间:单位ms;默认是1000ms
            timeoutInMilliseconds: 3000
  • java代码:
package com.mall.order.server.controller.test;

import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.sun.net.httpserver.Authenticator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.Arrays;

/**
 * 熔断器测试controller
 */
@RestController
@RequestMapping("/hystrix/test")
@DefaultProperties(defaultFallback = "defaultFallback")
public class HystrixTestController {

    @Autowired
    LoadBalancerClient loadBalancerClient;

//    @HystrixCommand(commandProperties = {
//           @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1000"),
//            @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),
//            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),
//            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),
//            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60")
//    })
    @HystrixCommand
    @GetMapping("/getProductList")
    public String getProductList(@RequestParam("number") int number){
        if(number % 2 == 0){
              return "success";
        }

        RestTemplate restTemplate = new RestTemplate();
        ServiceInstance serviceInstance = loadBalancerClient.choose("PRODUCT");
        String result = restTemplate.postForObject(
                String.format("http://%s:%s",serviceInstance.getHost(),serviceInstance.getPort())
                        + "/productClient/providerList",
                Arrays.asList("984325","894327"),
                String.class);
        return result;
//        throw new RuntimeException("error");
    }

    private String fallback(){
        return "访问时间过长,请稍后再试";
    }

    private String defaultFallback(){
        return "默认服务降级:访问时间过长,请稍后再试";
    }
}

  • 测试:number = 1可以调用了,因为超时时间是3s,大于线程等待的2秒
    在这里插入图片描述
2、自定义配置(HystrixCommandKey)
  • 我们给getProductList单独设置一个超时时间
server:
  port: 8082
spring:
  application:
    name: order
  cloud:
    config:
      discovery:
        enabled: true
        service-id: CONFIG
      profile: dev
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
  redis:
    host: localhost
    port: 6379
    password: 123456
    database: 3
vcap:
  application:
    instance_index: ${spring.cloud.config.profile}
hystrix:
  command:
    default:
      # 断路器相关配置
      circuitBreaker:
        # 是否开启断路器
        enabled: true
        # 滑动窗口的大小,默认为20
        requestVolumeThreshold: 10
        # 过多长时间,熔断器再次检测是否开启,默认为5000,即5s钟
        sleepWindowInMilliseconds: 10000
        # 错误率,默认50%
        errorThresholdPercentage: 60
      execution:
        isolation:
          thread:
            # 配置超时时间:单位ms;默认是1000ms
            timeoutInMilliseconds: 3000
    getProducts:
      execution:
        isolation:
          thread:
            # 配置超时时间:单位ms
            timeoutInMilliseconds: 1200
  • java代码使用commandKey指定:如果未指定commandKey值,会默认使用方法名getProductList进行匹配
 @HystrixCommand(commandKey = "getProducts")
    @GetMapping("/getProductList")
    public String getProductList(@RequestParam("number") int number){
        if(number % 2 == 0){
              return "success";
        }

        RestTemplate restTemplate = new RestTemplate();
        ServiceInstance serviceInstance = loadBalancerClient.choose("PRODUCT");
        String result = restTemplate.postForObject(
                String.format("http://%s:%s",serviceInstance.getHost(),serviceInstance.getPort())
                        + "/productClient/providerList",
                Arrays.asList("984325","894327"),
                String.class);
        return result;
//        throw new RuntimeException("error");
    }
  • 测试:因为自定义的超时时间是1.2秒,小于线程等待的2秒,所以触发了服务降级
    在这里插入图片描述

1.4、hystrix常用配置查看

  • 通过HystrixCommandProperties查看hystrix的相关配置
    在这里插入图片描述

三、hystrix可视化配置端

3.1、简单示例

  • hystrix-dashboard作用:可视化观察hystrix作用情况
1、pom文件引入hystrix-dashboard依赖
<!--引入hystrix dashboard可視化依賴-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>
2、启动类加上@EnableHystrixDashboard标签
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@MapperScan("com.mall.order.server.dao.mapper")
@EnableCircuitBreaker //开启熔断
@EnableHystrixDashboard
public class OrderServerApplication {

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

}

3.2、连接dashboard

1、开启服务访问hystrix首页
  • 配置完毕以后点击monitor按钮开始监控
    在这里插入图片描述
2、显示无法连接dashboard监控后台

在这里插入图片描述

3、解决问题
  • 引入健康监控依赖包,因为接口调用也和product服务有关,所以product和order服务最好均要引入spring-boot-starter-actuator健康监控依赖包

      <!--使用hystrix dashboard:必须引入健康监控依赖包-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
    
  • order服务的application.yml文件加入下列配置:

management:
  endpoints:
    web:
      exposure:
        include: 'hystrix.stream'
  • 最后monitor的url不要使用https方式:
    在这里插入图片描述
  • 为什么不能使用https:下面是springcloud官网给出的回答,我们需要将ssl证书添加到JVM中获取信任,否则后台日志会抛ssl证书授权错误,导致dashboard连接失败
    在这里插入图片描述
4、重新monitor:成功
  • 以上介绍的都是单例容错(只对一个服务进行容错),但是生产环境中需求是集群容错,集群容错使用Turbine,Turbine离不开hystrix。
    在这里插入图片描述

3.3、使用dashboard进行熔断监控

1、将前面的那两个请求在postman里面定时运行
  • 请求路径1:http://localhost:8082/hystrix/test/getProductList?number=1
  • 请求路径2:http://localhost:8082/hystrix/test/getProductList?number=2
  • 目的:方便观察dashboard面板的情况,手动点太累
  • postman点击一次连续发送多次请求
    在这里插入图片描述
2、观察dashboard面板

在这里插入图片描述

3、dashboard面板参数详解

在这里插入图片描述

  • 曲线中间有个圆,圆的大小表示流量,流量越大,圆越大;颜色越偏向红色,代表这个服务越不健康。
  • Host就是请求的频率
  • 图中testHystrixCommand5下来的百分比是失败率,旁边的六个数字根据颜色不同分别对应右上角那里的Success | Short-Circuited | Bad Request | Timeout | Rejected | Failure 的颜色。
  • Circuit就是熔断的状态,open就是熔断打开,closed就是关闭,还有半开half open
  • 曲线代表一段时间内,流量的相对变化
  • 官方文档介绍springcloud中文文档

3.4、@SpringCloudApplication标签

  • 等于下列三个标签,所以完全可以用@SpringCloudApplication标签替代这三个标签
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值