Spring Cloud Hystrix

介绍

image-20211116153418654

Hystrix 解决服务不可用时的解决方案

雪崩: 某个服务导致的问题导致的其他服务链路整个不可用

什么是hystrix

hystrix 是Netflix公司内部的服务容灾处理解决方案

雪崩效应

一个请求调用多个服务很常见。

img

某一个服务故障或者延迟会导致系统资源迅速耗尽或者系统出现不可信结果。

原因归结为三类:

  • 服务提供不可用
  • 重试导致的流量加大
  • 服务消费者不可用

解决方案

  • 请求缓存

    将一个请求与放回结果做缓存处理

  • 请求合并

    将相同的请求合并然后用批处理接口;

  • 服务隔离

    限制调用分布式资源,某一个调用服务出现问题不会影响服务调用。

  • 服务熔断

    牺牲局部服务,保证全体系统稳定措施;

  • 服务降级

    服务熔断以后可以断调用自己本地方法返回缺省值;

问题对应方案mapping

高并发场景:

  • 请求缓存:

    • 通过raids等技术通过缓存降低后续服务调用频率
  • 请求合并:

    • 比如查询多次不同的ID查询,可以合并为一次查询列表。
  • 平凡调用查询商品列表导致查询ID等小资源消耗的服务响应缓慢

    • 我们可以隔离微服务的资源。使用服务隔离
      • 线程池隔离
      • 信号量隔离
  • 服务熔断

    • 某个服务负载太高熔断处理
  • 服务降级

    • 后续服务无法调用返回缺省值,默认值

雪崩

异常服务可能会hold住一系列的请求

img

导致很多服务请求被控制住。导致系统崩溃。

img

总结,一个服务不可用导致一系列服务不可用。

解决方案

  • 请求缓存

  • 请求合并

  • 服务隔离

  • 服务熔断

  • 服务降级

    熔断后通常要降级

环境准备

要有服务调用

模拟并发场景

  • 在product list方法中中配置一个等待时间

    1
    2
    3
    4
    5
    try {
    TimeUnit.SECONDS.sleep(2);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
  • 在消费者中配置一个线程限制数量

    1
    2
    3
    4
    5
    server:
    port: ${SERVICE_PORT:18580}
    tomcat:
    threads:
    max: 10 # 视频的路径是max-threads:10 是错误的

    制造一个人为的等待超时

  • 另外写一个从order调用到productById的方法

  • 用JMeter做压力测试

    点击开始后再调用http://localhost:18580/order/product/2

    会发现这个作业也要等待10秒

JMeter 压力测试

性能压力测试工具

使用它来测试服务承受压力并且适用熔断。

地址: http://jmeter.apache.org/

下载一个: http://jmeter.apache.org/download_jmeter.cgi

  • 解压并运行

    在bin中的jmeter.sh 或者jmeter.jar 双击运行

  • 修改编码格式

    jmeter.properties

    1
    2
    3
    4
    5
    #language=en
    language=zh_CN

    #sampleresult.default.encoding=ISO-8859-1
    sampleresult.default.encoding=UTF-8
  • 添加一个setUp线程组

    50个线程每个线程执行50次

  • 在线程组中添加一个HTTP请求

    添加相应的参数

  • 在线程组中添加一个结果查看器

请求缓存

通过redis 做请求缓存来降低服务器延时

Hystrix自带的缓存是本地缓存无法满足集群需求。

并且Hystrix无法和第三方容器整合使用如MemChe

所以这里用redis做缓存

  • 用docker 启动一个

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    services:
    redis:
    image: redis:buster
    volumes:
    - ./datadir:/data
    - ./conf/redis.conf:/etc/redis/redis.conf
    - ./logs:/logs
    command: redis-server --requirepass 123456
    restart: always
    ports:
    - 16379:6379
    ~
  • 安装redis管理器RedisDesktopManager

  • 添加依赖包

    • org.springframework.boot spring-boot-starter-data-redis
  • 参考文档

    redis:https://docs.spring.io/spring-data/redis/docs/2.5.5/reference/html/#reference

  • 修改application.yml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    spring:
    redis:
    host: 127.0.0.1 #host
    port: 16379 #port
    timeout: 10000 #超时时间
    database: 0 #数据库名称
    password: 123456
    lettuce:
    pool:
    max-active: 64 #最大连接数
    max-wait: 10000 #最大等待时间
    max-idle: 16 #最大空闲
    min-idle: 8 #最小空闲
  • pom.xml 中添加

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <dependency>
    <groupId>org.springframework.boot </groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>${spring-boot.version}</version>
    </dependency>
    <dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    </dependency>

    必须要使用commons-pool2才能正常调用redis这相当于mysql driver

  • 启动类要激活@EnableCaching 注解

  • 编写RedisConfig 在 org.akachi.eurekademo.order.config中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    package org.akachi.eurekademo.order.config;

    import com.fasterxml.jackson.annotation.JsonAutoDetect;
    import com.fasterxml.jackson.annotation.JsonTypeInfo;
    import com.fasterxml.jackson.annotation.PropertyAccessor;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.cache.RedisCacheConfiguration;
    import org.springframework.data.redis.cache.RedisCacheManager;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.RedisSerializationContext;
    import org.springframework.data.redis.serializer.RedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;

    import java.time.Duration;

    import static org.springframework.data.redis.cache.RedisCacheConfiguration.defaultCacheConfig;

    /**
    * @Author akachi
    * @Email zsts@hotmail.com
    * @Date 2021/11/17 20:07
    */
    @Configuration
    public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
    // 为了开发方便,一般使用<String, Object>
    RedisTemplate<String, Object> template = new RedisTemplate<>();
    template.setConnectionFactory(redisConnectionFactory);

    // Json序列化配置
    Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
    StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

    // 设置key的序列化方式
    template.setKeySerializer(stringRedisSerializer);

    // 设置hashkey的序列化方式
    template.setHashKeySerializer(stringRedisSerializer);

    // 设置value的序列化方式
    template.setValueSerializer(jackson2JsonRedisSerializer);

    // 设置hashvalue的序列化方式
    template.setHashValueSerializer(jackson2JsonRedisSerializer);

    template.afterPropertiesSet();

    return template;
    }

    @Bean
    public RedisCacheManager myRedisCacheManager(RedisConnectionFactory redisConnectionFactory) {
    RedisSerializer<String> redisSerializer = new StringRedisSerializer();

    Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

    ObjectMapper om = new ObjectMapper();
    om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance ,
    ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
    jackson2JsonRedisSerializer.setObjectMapper(om);

    // 配置序列化(解决乱码的问题)
    RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
    .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
    .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
    .disableCachingNullValues();

    RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(config).build();

    return cacheManager;
    }
    }

  • 业务类上加入缓存

    1
    @Cacheable(cacheNames = "orderService:product:list")
  • LocalDateTime 类型会报错所以要加上注解

    1
    2
    @JsonDeserialize(using = LocalDateTimeDeserializer.class)
    @JsonSerialize(using = LocalDateTimeSerializer.class)

总结:

  1. 引入包
  2. 搭建redis环境
  3. 创建@Configuration
    1. 创建@Bean RedisTemplate
    2. 创建@Bean RedisCacheManager
  4. 创建@EnableCacheing
  5. 在需要缓存的地方引入@Cacheable注解

请求合并

image-20211118113207766

请求合并的情况下必须有一个基础等待时间,比如100ms,在这个等待时间中等待其他的请求来合并请求

多个请求ID一次调用。

  • 添加依赖pom.xml

    1
    2
    3
    4
    5
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    <version>2.2.9.RELEASE</version>
    </dependency>
  • 创建一个product/{id}接口和一个product/listbyid?ids=[]接口

  • 在order端也实现他们之间的调用

  • 通过@HystrixCommand@HystrixCollapser 读音:kə'læpsər(折叠器) 进行分发

  • 原来product接口直接返回Product 现在需要修改为Future java.util.concurrent.Future

  • 在批量方法上加入@HystrixCommand

  • 在单个方法上加入@HystrixCollapser

    1
    2
    3
    4
    5
    6
    @HystrixCollapser(batchMethod = "productListByIds",
    scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL,
    collapserProperties = {
    @HystrixProperty(name = "timerDelayInMilliseconds", value = "20"),
    @HystrixProperty(name = "maxRequestsInBatch", value = "200")
    })
  • 在启动类开启熔断器注解@EnableHystrix 封装了 @EnableCircuitBreaker

image-20211118162345764

请求合并成功!

服务隔离

默认情况下服务的线程资源是统一的。

某些高消费的服务请求的等待会占用线程池,导致低消费的服务也进入线程池的资源等待列表中。

服务隔离 就是通过限制每个服务占用资源的方式来保证就算高消耗服务占用了资源低消耗服务仍然能够拿到足够完成请求的资源。

@HystrixCommand 注解参数说明:

image-20211119111334999

线程池隔离

  • 首先添加依赖pom.xml

    1
    2
    3
    4
    5
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    <version>2.2.9.RELEASE</version>
    </dependency>
  • 在启动类开启熔断器注解@EnableHystrix 封装了 @EnableCircuitBreaker

  • 在list*这个在之前就就已经写了等待两秒了* 添加日志打印线程名

  • java代码 为getProducts 和getProduct 加入了注解并且加入了一个托底方法getProductsFallback

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    @Override
    //@Cacheable(cacheNames = "orderService:product:list")
    //声明它是需要做服务容错的
    //然后最服务隔离
    @HystrixCommand(groupKey = "order-productService-listPool", //组名称,只要组名称相同就会泡在一个池里面
    commandKey = "getProducts", //结构名称默认是方法名
    threadPoolKey = "order-productService-listPool",
    commandProperties = {
    //默认超时时间 1000ms
    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000")
    },
    threadPoolProperties = {
    // 現場大小
    @HystrixProperty(name = "coreSize", value = "6"),
    // 隊列等待默認值默認-1
    @HystrixProperty(name = "maxQueueSize", value = "100"),
    // 綫程存貨時間,默認1min
    @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
    // 超出隊列等待執行拒絕策略
    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "2")
    },
    fallbackMethod = "getProductsFallback" //拒绝的默认值
    )
    public List<Product> getProducts() {
    ResponseEntity<List<Product>> responseEntity = restTemplateBalancer.exchange(
    "http://EUREKADEMO-SERVICE-PRODUCT/product/list",
    HttpMethod.GET,
    null,
    new ParameterizedTypeReference<List<Product>>() {
    });
    return responseEntity.getBody();
    }

    //不查询productList的数据
    private List<Product> getProductsFallback(){
    //托底数据
    List<Product> products = new ArrayList<>();
    products.add(new Product().setCode("-1").setName("無法獲得數據").setManufacture("-1"));
    return products;
    }

    @Override
    @HystrixCommand(groupKey = "order-productService-singlePool", //组名称,只要组名称相同就会泡在一个池里面
    commandKey = "getProducts", //结构名称默认是方法名
    threadPoolKey = "order-productService-singlePool",
    commandProperties = {
    //默认超时时间 1000ms
    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000")
    },
    threadPoolProperties = {
    // 現場大小
    @HystrixProperty(name = "coreSize", value = "3"),
    // 隊列等待默認值默認-1
    @HystrixProperty(name = "maxQueueSize", value = "100"),
    // 綫程存貨時間,默認1min
    @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
    // 超出隊列等待執行拒絕策略
    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "2")
    }
    )
    public Product getProduct(@PathVariable("id") String code) {
    ResponseEntity<Product> responseEntity = restTemplateBalancer.exchange(
    "http://EUREKADEMO-SERVICE-PRODUCT/product/" + code,
    HttpMethod.GET,
    null,
    new ParameterizedTypeReference<Product>() {
    });
    return responseEntity.getBody();
    }
  • getProductsFallback 就是服务降级所用的具体方法

  • 使用Apache JMeter来进行测试

    1
    maxQueueSize

信号量隔离

线程池隔离会比较平凡的创建和销毁线程

image-20211119104337746

  • 添加maven依赖

  • 修改注解

  • 新增一个requestsService来测试新功能

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    @Override
    //@Cacheable(cacheNames = "orderService:product:list")
    //声明它是需要做服务容错的
    //然后最服务隔离
    @HystrixCommand(
    commandProperties = {
    //默认超时时间 1000ms
    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000"),
    // 信号量隔离
    @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY , value = "SEMAPHORE"),
    // 信号量最大并发,调小一些方便模拟高并发
    @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value="6")
    },
    fallbackMethod = "getProductsFallback" //拒绝的默认值
    )
    public List<Product> getProducts() {
    log.warn(Thread.currentThread().getName() + "-getProducts");
    ResponseEntity<List<Product>> responseEntity = restTemplateBalancer.exchange(
    "http://EUREKADEMO-SERVICE-PRODUCT/product/list",
    HttpMethod.GET,
    null,
    new ParameterizedTypeReference<List<Product>>() {
    });
    return responseEntity.getBody();
    }

    //不查询productList的数据
    private List<Product> getProductsFallback(){
    //托底数据
    List<Product> products = new ArrayList<>();
    products.add(new Product().setCode("-1").setName("無法獲得數據").setManufacture("-1"));
    return products;
    }

    @Override
    @HystrixCommand(
    commandProperties = {
    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000"),
    // 信号量隔离
    @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY,
    value = "SEMAPHORE"),
    // 信号量最大并发,调小一些方便模拟高并发
    @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value = "3")
    }
    )
    public Product getProduct(@PathVariable("id") String code) {
    log.warn(Thread.currentThread().getName() + "-getProduct");
    ResponseEntity<Product> responseEntity = restTemplateBalancer.exchange(
    "http://EUREKADEMO-SERVICE-PRODUCT/product/" + code,
    HttpMethod.GET,
    null,
    new ParameterizedTypeReference<Product>() {
    });
    return responseEntity.getBody();
    }

    主要在HystrixCommand中指定了信号量调用方式

  • 用apache JMater测试 线程数量设置为6

    再用浏览器请求只能拿到托底数据。

    要注意使用apache JMater时选择具体的线程组右键执行。

区别

  • 线程隔离

    • 请求线程和Provider线程不是用一条线程

    • 支持超时可直接放回

    • 支持熔断

    • 隔离原理:每个服务单独线程池

    • 支持同步和异步两种方式

    • 资源消耗大上下文切换、排队、调度等、容易造成负载高

    • 无法传递 HTTP Header

      • 可传递HTTP Header。

    image-20211119125356434

  • 信号量隔离

    • 请求和线程使用同一个Provider线程。
  • 不支持超时

    • 支持熔断,信号量达到MaxConcurrentRequests后再请求返回fallbac进行熔断。
  • 隔离原理:通过信号量计数器;

    • 同步调用,不支持异步
  • 资源消耗小,只是个计数器。

适用于请求量较大的服务

服务熔断

服务熔断一般是指软件系统中,由于某些原因出现了过载现象,为了防止整个系统故障,从而采用的一种保护措施,所以很多地方吧熔断称之为过载保护。

image-20211119151327283

  • 如果服务无法正常请求则开启熔断机制并且返回fallback数据。
  • 一段时间内重试provider如果重试失败率低于周期失败率阈值则关闭熔断。
  • 关键性参数
  • 熔断周期
  • 熔断失败率阈值
  • 重试周期
  • 添加依赖 hystrix

  • 在业务上声明注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    package org.akachi.eurekademo.order.service.impl;

    import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
    import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
    import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager;
    import lombok.extern.slf4j.Slf4j;
    import org.akachi.eurekademo.order.service.ProductService;
    import org.akachi.eurekademo.product.model.Product;
    import org.springframework.stereotype.Service;

    import java.util.List;

    /**
    * @Author akachi
    * @Email zsts@hotmail.com
    * @Date 2021/11/17 21:19
    */
    @Service("ProductServiceRequestsImpl")
    @Slf4j
    public class ProductServiceRequestsImpl extends ProductServiceImpl implements ProductService {

    @Override
    @HystrixCommand(commandProperties = {
    // 多少个请求挂掉出发熔断
    @HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD,
    value="10"),
    //戳五率大于50%就启动熔断器,然后for循环重试请求,
    @HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE,
    value="50"),
    // 熔断重试时间
    @HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS,
    value="5000")
    },
    fallbackMethod = "getProductsFallback"
    )
    public List<Product> getProducts() {
    log.warn("run products");
    return super.getProducts();
    }
    @Override
    public List<Product> getProductsFallback(){
    log.warn("run getProductsFallback");
    return super.getProductsFallback();
    }
    }
  • 修改application.yml

    1
    2
    3
    spring:
    main:
    allow-bean-definition-overriding: true
  • 注解参数解析

    • circuitBreaker.requestVolumeThreshold

      统计熔断颗粒度,默认20

    • circuitBreaker.errorThresholdPercentage

      熔断失败概率

    • circuitBreaker.sleepWindowInMilliseconds

      多久重试一次 默认5s

  • 熔断成功

    image-20211119163557715

服务降级

不做隔離、不做熔斷直接使用fallbackMethod就是服務降級

  • @HystrixCommand(fallbackMethod = "getProductsFallback")

Feign雪崩处理

参考spring Cloud Feign-学习笔记

服务监控

image-20211126151235392

Actuator

  • 添加依赖

    1
    2
    3
    4
    5
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    <version>2.5.5</version>
    </dependency>
  • 暴露hystrix.stream端点

    1
    2
    3
    4
    5
    management:
    endpoints:
    web:
    exposure:
    include: hystrix.stream # '*'也行
  • 通过actuator可以看所有的端点

    /actuator/hystrix.stream

Tubine

可以把其他端点都聚过来

监控中心

image-20211130100645984

添加一个dashboard的依赖

  • spring-cloud-starter-netflix-hystrix-dashboard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.5.5</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>

聚合监控

在多个项目中启用监控中心,监控起来会很麻烦。

另外做了大量测试只有用Hystrix框架的调用才可以监控。

image-20211130100931613

聚合监控需要搭建一个独立的项目

创建新项目 hystrix-turbine
  1. 添加Netflix-turebine依赖
  2. application启动类加入@EnableTurbine
  3. 配置文件中注册如eureka,通过服务名配置turbine监控
  • application.yml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    server:
    port: ${SERVICE_PORT:18080}
    spring:
    application:
    name: hystrix-turbine
    cloud:
    inetutils:
    ignoredInterfaces: ['VM*','ingress'] # 必须过滤掉VMware 因为会豁达86.1
    preferred-networks:
    - ${NET_DOMAIN:192.168/16} #必须只选择192网段的否则会选择一些虚拟机网卡
    eureka:
    instance:
    prefer-ip-address: true
    # instance-id: ${spring.cloud.client.ip-address}:${server.port} #必须加入名字和端口否则ID会相同
    client:
    register-with-eureka: true # 将自己注册到注册中心 默认 true
    fetch-registry: true # 是否从注册中心获取服务注册信息 默认true
    service-url:
    defaultZone: http://root:123456@localhost:18761/eureka/,http://root:123456@localhost:18762/eureka/,http://root:123456@localhost:18763/eureka/
    hystrix:
    dashboard:
    proxy-stream-allow-list: "*"
    turbine:
    # 可调用的服务用逗号分割
    app-config: eurekademo-service-order-concurrency-test,eurekademo-service-product
    # 指定集群名
    cluster-name-expression: "'default'"
  • pom.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
    <artifactId>eurekademo</artifactId>
    <groupId>org.akachi</groupId>
    <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.akachi.eurekademo</groupId>
    <artifactId>hystrix-turbine</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>order-concurrency-test</name>
    <description>Demo project for Spring Boot</description>
    <properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
    <java.version>11</java.version>
    <spring-cloud.version>2020.0.4</spring-cloud.version>
    <spring-boot.version>2.5.5</spring-boot.version>
    <netflix-eureka-server.version>3.0.4</netflix-eureka-server.version>
    <netflix-netflix.version>2.2.10.RELEASE</netflix-netflix.version>
    </properties>
    <dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>${spring-boot.version}</version>
    </dependency>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    <version>${netflix-eureka-server.version}</version>
    </dependency>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    <version>2.2.10.RELEASE</version>
    </dependency>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
    <version>2.2.10.RELEASE</version>
    </dependency>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-turbine</artifactId>
    <version>2.2.10.RELEASE</version>
    </dependency>
    </dependencies>

    <build>
    <plugins>
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
    </plugins>
    </build>
    </project>
  • Turbine.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    package org.akachi.eurekademo.turbine;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.hystrix.EnableHystrix;
    import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
    import org.springframework.cloud.netflix.turbine.EnableTurbine;

    @EnableHystrix
    @EnableHystrixDashboard
    @SpringBootApplication
    @EnableTurbine
    public class TurbineApplication {

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

  • 通过http://localhost:18080/turbine.stream 查看 流

  • 通过http://localhost:18080/hystrix 监控

  • image-20211130112026156

  • 可以对多个服务进行监控

参考资料

视频教程: https://www.bilibili.com/video/BV1hf4y1r7Kb?p=2

JMeter: http://jmeter.apache.org/

hystrix参考资料: https://docs.spring.io/spring-cloud-netflix/docs/2.2.10.RELEASE/reference/html/