【Spring Cloud Netflix 温故而知新】(九)SpringCloud Netflix Hystrix 实现微服务容错 | Eddie'Blog
【Spring Cloud Netflix 温故而知新】(九)SpringCloud Netflix Hystrix 实现微服务容错

【Spring Cloud Netflix 温故而知新】(九)SpringCloud Netflix Hystrix 实现微服务容错

eddie 264 2021-12-13

目录

12.1.1 SpringCloud Netflix Hystrix 概览

12.1.1.1 Hystrix 是什么、设计目标是什么

  • Hystrix 是一个库,通过添加“延迟容忍”和“容错逻辑”,帮助你控制这些分布式服务之间的交互
  • Hystrix 通过隔离服务之间的访问点、停止级联失败和提供回退选项来实现服务之间的容错
  • Hystrix 有四大设计目标
    • 对客户端访问的延迟和故障进行保护和控制
    • 在复杂的分布式系统中阻止级联故障
    • 快速失败,快速恢复
    • 兜底回退,尽可能优雅的降级
  • Hystrix 解决了什么问题
    • 复杂分布式系统中服务之间存在许多依赖项,依赖项可能会存在故障,如果不做故障隔离,整个服务可能会被拖垮
  • Hystrix 是如何实现它的目标的
    • 对依赖项(服务)进行包装代理,不直接与依赖项交互
    • 调用超时时间允许自行设定,超时之后立刻熔断报错
    • 每一个依赖项都在自己的空间内(线程池或信号量隔离),依赖项之间不存在干扰
    • 请求依赖项失败后,可以选择出错或者是兜底回退

12.2.1 使用注解方式实现服务的容错、降级

12.2.1.1 Hystrix 的三种模式

  • Hystrix 的断路器模式和后备策略模式
    • 断路器模式:设置超时或者失败等熔断策略
    • 后备策略模式(兜底):断路器模式出发之后,如果存在后备策略,则执行后备
    • 舱壁模式:比如货船为了进行防治漏水和火灾的扩散,会将货仓分隔为多个,当发生灾难时,将所在的货仓进行隔离就可以降低整条船的风险
      • 通过为每个服务分别制定线程池

在这里插入图片描述

12.2.1.2 使用 HystrixCommand 注解实践

Maven 依赖

 <!-- 集成 hystrix -->
 <dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
 </dependency>

添加 @EnableCircuitBreake 注解

@RefreshScope
@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
@EnableCircuitBreaker // 启动 hystrix
public class NacosClientApplication {

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

}

三种模式集于一身

package com.edcode.commerce.service.hystrix;

import com.edcode.commerce.service.NacosClientService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.List;

/**
 * @author eddie.lee
 * @blog blog.eddilee.cn
 * @description 使用 HystrixCommand 注解
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class UseHystrixCommandAnnotation {

    private final NacosClientService nacosClientService;

    @HystrixCommand(
            // 用于对 Hystrix 命令进行分组, 分组之后便于统计展示于仪表盘、上传报告和预警等等
            // 内部进行度量统计时候的分组标识, 数据上报和统计的最小维度就是 groupKey
            groupKey = "NacosClientService",
            // HystrixCommand 的名字, 默认是当前类的名字, 主要方便 Hystrix 进行监控、报警等
            commandKey = "NacosClientService",
            // 舱壁模式
            threadPoolKey = "NacosClientService",
            // 后备模式
            fallbackMethod = "getNacosClientInfoFallback",
            // 断路器模式
            commandProperties = {
                    // 超时时间, 单位毫秒, 超时进 fallback
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500"),
                    // 判断熔断的最少请求数, 默认是10; 只有在一定时间内请求数量达到该值, 才会进行成功率的计算
                    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
                    // 熔断的阈值默认值 50, 表示在一定时间内有50%的请求处理失败, 会触发熔断
                    @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "10"),
            },
            // 舱壁模式
            threadPoolProperties = {
                    @HystrixProperty(name = "coreSize", value = "30"),
                    @HystrixProperty(name = "maxQueueSize", value = "101"),
                    @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "15"),
                    // 在时间窗口中, 收集统计信息的次数; 在 1440ms 的窗口中一共统计 12 次
                    @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "12"),
                    // 时间窗口, 从监听到第一次失败开始计时
                    @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "1440")
            }
    )
    public List<ServiceInstance> getNacosClientInfo(String serviceId) {

        // 测试 UseHystrixCommandAnnotation 的超时, 看是否兜底
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            // ...
        }

        log.info("使用hystrix命令注释获取nacos客户端信息: [{}], [{}]",
                serviceId, 
                Thread.currentThread().getName()
        );
        return nacosClientService.getNacosClientInfo(serviceId);
    }

	/**
	 * getNacosClientInfo 的兜底策略 - Hystrix 后备模式
	 */
    public List<ServiceInstance> getNacosClientInfoFallback(String serviceId) {

        log.warn("无法获取nacos客户端,触发hystrix回退: [{}], [{}]",
                serviceId,
                Thread.currentThread().getName()
        );
        return Collections.emptyList();
    }

}

在请求 getNacosClientInfo() 方法时候,会睡眠 2000ms,在断路器设置了超时时间是 1500ms.

Hystrix 控制层

package com.edcode.commerce.controller;

import com.edcode.commerce.service.hystrix.UseHystrixCommandAnnotation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
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 java.util.List;
import lombok.RequiredArgsConstructor;

/**
 * @author eddie.lee
 * @blog blog.eddilee.cn
 * @description
 */
@Slf4j
@RestController
@RequestMapping("/hystrix")
@RequiredArgsConstructor
public class HystrixController {

	private final UseHystrixCommandAnnotation hystrixCommandAnnotation;

    @GetMapping("/hystrix-command-annotation")
    public List<ServiceInstance> getNacosClientInfoUseAnnotation(@RequestParam String serviceId) {

        log.info("请求nacos客户端信息使用注解: [{}], [{}]",
                serviceId,
                Thread.currentThread().getName()
        );

        return hystrixCommandAnnotation.getNacosClientInfo(serviceId);
    }

}

hystrix.http

### 根据提供的 serviceId 获取实例信息
GET http://127.0.0.1:8000/scacommerce-nacos-client/hystrix/hystrix-command-annotation?serviceId=sca-commerce-nacos-client
Content-Type: application/json

###

日志分析

2021-11-18 21:44:05.602  INFO [sca-commerce-nacos-client,f228a4124fd5d0dd,f228a4124fd5d0dd,true] 10092 --- [nio-8000-exec-3] c.e.c.controller.HystrixController       : 请求nacos客户端信息使用注解: [sca-commerce-nacos-client], [http-nio-8000-exec-3]
2021-11-18 21:44:07.108  INFO [sca-commerce-nacos-client,f228a4124fd5d0dd,518b3e22d2721893,true] 10092 --- [ClientService-2] c.e.c.s.h.UseHystrixCommandAnnotation    : 使用hystrix命令注解获取nacos客户端信息: [sca-commerce-nacos-client], [hystrix-NacosClientService-2]
2021-11-18 21:44:07.108  INFO [sca-commerce-nacos-client,f228a4124fd5d0dd,518b3e22d2721893,true] 10092 --- [ClientService-2] c.e.commerce.service.NacosClientService  : 请求nacos客户端获取服务实例信息: [sca-commerce-nacos-client]
2021-11-18 21:44:07.108  WARN [sca-commerce-nacos-client,f228a4124fd5d0dd,02cf47e778be3255,true] 10092 --- [ HystrixTimer-1] c.e.c.s.h.UseHystrixCommandAnnotation    : 无法获取nacos客户端,触发hystrix回退: [sca-commerce-nacos-client], [HystrixTimer-1]
  • 请求nacos客户端信息使用注解: [sca-commerce-nacos-client], [http-nio-8000-exec-3]
    • 服务实例名,Java里面线程
  • 使用hystrix命令注解获取nacos客户端信息: [sca-commerce-nacos-client], [hystrix-NacosClientService-2]
    • 服务实例名,hystrix 线程 (舱壁模式
  • 无法获取nacos客户端,触发hystrix回退: [sca-commerce-nacos-client], [HystrixTimer-1]
    • 服务实例名,hystrix 线程 (后备模式-兜底

12.4.1 使用编程方式实现服务的容错、降级(Hystrix 舱壁模式)

package com.edcode.commerce.service.hystrix;

import com.edcode.commerce.service.NacosClientService;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandProperties;
import com.netflix.hystrix.HystrixThreadPoolKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;

import java.util.Collections;
import java.util.List;

import static com.netflix.hystrix.HystrixCommandProperties.ExecutionIsolationStrategy.THREAD;

/**
 * @author eddie.lee
 * @blog blog.eddilee.cn
 * @description NacosClientService 实现包装
 *
 * Hystrix 舱壁模式:
 *  1. 线程池
 *  2. 信号量: 算法 + 数据结构, 有限状态机
 *
 */
@Slf4j
public class NacosClientHystrixCommand extends HystrixCommand<List<ServiceInstance>> {

    /** 需要保护的服务 */
    private final NacosClientService nacosClientService;

    /** 方法需要传递的参数 */
    private final String serviceId;

    public NacosClientHystrixCommand(NacosClientService nacosClientService, String serviceId) {

        super(
                Setter.withGroupKey(
                        HystrixCommandGroupKey.Factory.asKey("NacosClientService"))
                        .andCommandKey(HystrixCommandKey.Factory.asKey("NacosClientHystrixCommand"))
                        .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("NacosClientPool"))
                        // 线程池 key 配置
                        .andCommandPropertiesDefaults(
                                HystrixCommandProperties.Setter()
                                        // 线程池隔离策略
                                        .withExecutionIsolationStrategy(THREAD)
                                        // 开启降级
                                        .withFallbackEnabled(true)
                                        // 开启熔断器 : 比如当出现异常时候,就会熔断
                                        .withCircuitBreakerEnabled(true)
                        )
        );

        // 可以配置信号量隔离策略
//        Setter semaphore =
//                Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("NacosClientService"))
//                .andCommandKey(HystrixCommandKey.Factory.asKey("NacosClientHystrixCommand"))
//                .andCommandPropertiesDefaults(
//                        HystrixCommandProperties.Setter()
//                        .withCircuitBreakerRequestVolumeThreshold(10)
//                        .withCircuitBreakerSleepWindowInMilliseconds(5000)
//                        .withCircuitBreakerErrorThresholdPercentage(50)
//                        .withExecutionIsolationStrategy(SEMAPHORE)  // 指定使用信号量隔离
//                        //.....
//                );

        this.nacosClientService = nacosClientService;
        this.serviceId = serviceId;
    }

    /**
     * 要保护的方法调用写在 run 方法中
     * */
    @Override
    protected List<ServiceInstance> run() throws Exception {
        log.info("使用Hystrix命令中的 NacosClientService 获取服务实例: [{}], [{}]",
                this.serviceId, Thread.currentThread().getName());
        return this.nacosClientService.getNacosClientInfo(this.serviceId);
    }

    /**
     * 降级处理策略
     * */
    @Override
    protected List<ServiceInstance> getFallback() {
        log.warn("NacosClientService 运行错误: [{}], [{}]",
                this.serviceId, Thread.currentThread().getName());
        return Collections.emptyList();
    }
}

12.5.1 使用编程方式实现服务的容错、降级(测试)

12.5.1.1 修改 Service 直接抛出异常

@Slf4j
@Service
@RequiredArgsConstructor
public class NacosClientService {

    private final DiscoveryClient discoveryClient;

    /**
     * 打印 Nacos Client 信息到日志
     * @param serviceId
     * @return
     */
    public List<ServiceInstance> getNacosClientInfo(String serviceId) {

        // UseHystrixCommandAnnotation 测试超时
//        try {
//            Thread.sleep(2000);
//        } catch (InterruptedException e) {
//            //
//        }

        // NacosClientHystrixCommand 测试熔断
        throw new RuntimeException("has some error");

//        log.info("请求nacos客户端获取服务实例信息: [{}]", serviceId);
//        return discoveryClient.getInstances(serviceId);
    }

}

12.5.1.2 控制层开放 Api

package com.edcode.commerce.controller;

import com.alibaba.fastjson.JSON;
import com.edcode.commerce.service.NacosClientService;
import com.edcode.commerce.service.hystrix.NacosClientHystrixCommand;
import com.edcode.commerce.service.hystrix.UseHystrixCommandAnnotation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
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 java.util.List;
import java.util.concurrent.Future;

import lombok.RequiredArgsConstructor;
import rx.Observable;

/**
 * @author eddie.lee
 * @blog blog.eddilee.cn
 * @description
 */
@Slf4j
@RestController
@RequestMapping("/hystrix")
@RequiredArgsConstructor
public class HystrixController {

	private final UseHystrixCommandAnnotation hystrixCommandAnnotation;

    private final NacosClientService nacosClientService;

    @GetMapping("/simple-hystrix-command")
    public List<ServiceInstance> getServiceInstanceByServiceId(@RequestParam String serviceId) throws Exception {

        // 第一种方式
        List<ServiceInstance> serviceInstances01 = new NacosClientHystrixCommand(nacosClientService, serviceId).execute();    // 同步阻塞

        log.info("使用execute获取服务实例: [{}], [{}]", JSON.toJSONString(serviceInstances01), Thread.currentThread().getName());

        // 第二种方式 (最为广泛使用)
        List<ServiceInstance> serviceInstances02;
        Future<List<ServiceInstance>> future = new NacosClientHystrixCommand(nacosClientService, serviceId).queue();      // 异步非阻塞
        // 这里可以做一些别的事, 需要的时候再去拿结果
        serviceInstances02 = future.get();
        log.info("使用队列获取服务实例: [{}], [{}]", JSON.toJSONString(serviceInstances02), Thread.currentThread().getName());

        // 第三种方式
        Observable<List<ServiceInstance>> observable = new NacosClientHystrixCommand(nacosClientService, serviceId).observe();        // 热响应调用
        List<ServiceInstance> serviceInstances03 = observable.toBlocking().single();
        log.info("使用“观察”获取服务实例: [{}], [{}]", JSON.toJSONString(serviceInstances03), Thread.currentThread().getName());

        // 第四种方式
        Observable<List<ServiceInstance>> toObservable = new NacosClientHystrixCommand(nacosClientService, serviceId).toObservable();        // 异步冷响应调用
        List<ServiceInstance> serviceInstances04 = toObservable.toBlocking().single();
        log.info("使用toObservable获取服务实例: [{}], [{}]", JSON.toJSONString(serviceInstances04), Thread.currentThread().getName());

        // execute = queue + get
        return serviceInstances01;
    }

}

12.5.1.3 hystrix.http 测试

### 根据提供的 serviceId 获取实例信息
GET http://127.0.0.1:8000/scacommerce-nacos-client/hystrix/simple-hystrix-command?serviceId=sca-commerce-nacos-client
Content-Type: application/json
2021-11-29 22:17:47.747  INFO [sca-commerce-nacos-client,57b1ebe42781a384,bf155b6376414e6e,true] 17248 --- [cosClientPool-9] c.e.c.s.h.NacosClientHystrixCommand      : 使用Hystrix命令中的 NacosClientService 获取服务实例: [sca-commerce-nacos-client], [hystrix-NacosClientPool-9]
2021-11-29 22:17:47.749  WARN [sca-commerce-nacos-client,57b1ebe42781a384,bf155b6376414e6e,true] 17248 --- [cosClientPool-9] c.e.c.s.h.NacosClientHystrixCommand      : NacosClientService 运行错误: [sca-commerce-nacos-client], [hystrix-NacosClientPool-9]
2021-11-29 22:17:47.749  INFO [sca-commerce-nacos-client,57b1ebe42781a384,57b1ebe42781a384,true] 17248 --- [nio-8000-exec-9] c.e.c.controller.HystrixController       : 使用execute获取服务实例: [[]], [http-nio-8000-exec-9]
2021-11-29 22:17:47.750  INFO [sca-commerce-nacos-client,57b1ebe42781a384,2fa9b27784e3eb51,true] 17248 --- [osClientPool-10] c.e.c.s.h.NacosClientHystrixCommand      : 使用Hystrix命令中的 NacosClientService 获取服务实例: [sca-commerce-nacos-client], [hystrix-NacosClientPool-10]
2021-11-29 22:17:47.752  WARN [sca-commerce-nacos-client,57b1ebe42781a384,2fa9b27784e3eb51,true] 17248 --- [osClientPool-10] c.e.c.s.h.NacosClientHystrixCommand      : NacosClientService 运行错误: [sca-commerce-nacos-client], [hystrix-NacosClientPool-10]
2021-11-29 22:17:47.754  INFO [sca-commerce-nacos-client,57b1ebe42781a384,57b1ebe42781a384,true] 17248 --- [nio-8000-exec-9] c.e.c.controller.HystrixController       : 使用队列获取服务实例: [[]], [http-nio-8000-exec-9]
2021-11-29 22:17:47.754  INFO [sca-commerce-nacos-client,57b1ebe42781a384,e2fb5844ebfe5c56,true] 17248 --- [osClientPool-10] c.e.c.s.h.NacosClientHystrixCommand      : 使用Hystrix命令中的 NacosClientService 获取服务实例: [sca-commerce-nacos-client], [hystrix-NacosClientPool-10]
2021-11-29 22:17:47.756  WARN [sca-commerce-nacos-client,57b1ebe42781a384,e2fb5844ebfe5c56,true] 17248 --- [osClientPool-10] c.e.c.s.h.NacosClientHystrixCommand      : NacosClientService 运行错误: [sca-commerce-nacos-client], [hystrix-NacosClientPool-10]
2021-11-29 22:17:47.757  INFO [sca-commerce-nacos-client,57b1ebe42781a384,57b1ebe42781a384,true] 17248 --- [nio-8000-exec-9] c.e.c.controller.HystrixController       : 使用“观察”获取服务实例: [[]], [http-nio-8000-exec-9]
2021-11-29 22:17:47.757  INFO [sca-commerce-nacos-client,57b1ebe42781a384,db5849263b52f89d,true] 17248 --- [osClientPool-10] c.e.c.s.h.NacosClientHystrixCommand      : 使用Hystrix命令中的 NacosClientService 获取服务实例: [sca-commerce-nacos-client], [hystrix-NacosClientPool-10]
2021-11-29 22:17:47.759  WARN [sca-commerce-nacos-client,57b1ebe42781a384,db5849263b52f89d,true] 17248 --- [osClientPool-10] c.e.c.s.h.NacosClientHystrixCommand      : NacosClientService 运行错误: [sca-commerce-nacos-client], [hystrix-NacosClientPool-10]
2021-11-29 22:17:47.759  INFO [sca-commerce-nacos-client,57b1ebe42781a384,57b1ebe42781a384,true] 17248 --- [nio-8000-exec-9] c.e.c.controller.HystrixController       : 使用toObservable获取服务实例: [[]], [http-nio-8000-exec-9]

12.6.1 HystrixCommand, 隔离策略是基于信号量实现的

12.6.1.1 信号量实现

package com.edcode.commerce.service.hystrix;

import com.alibaba.fastjson.JSON;
import com.edcode.commerce.service.NacosClientService;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandProperties;
import com.netflix.hystrix.HystrixObservableCommand;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import rx.Observable;
import rx.Subscriber;

import java.util.Collections;
import java.util.List;

/**
 * @author eddie.lee
 * @blog blog.eddilee.cn
 * @description HystrixCommand, 隔离策略是基于信号量实现的
 */
@Slf4j
public class NacosClientHystrixObservableCommand extends HystrixObservableCommand<List<ServiceInstance>> {

    /** 要保护的服务 */
    private final NacosClientService nacosClientService;

    /** 方法需要传递的参数 */
    private final List<String> serviceIds;

    public NacosClientHystrixObservableCommand(NacosClientService nacosClientService, List<String> serviceIds) {
        super(
                HystrixObservableCommand.Setter
                        .withGroupKey(HystrixCommandGroupKey
                                .Factory.asKey("NacosClientService"))
                        .andCommandKey(HystrixCommandKey
                                .Factory.asKey("NacosClientHystrixObservableCommand"))
                        .andCommandPropertiesDefaults(
                                HystrixCommandProperties.Setter()
                                        .withFallbackEnabled(true)          // 开启降级
                                        .withCircuitBreakerEnabled(true)    // 开启熔断器
                        )
        );

        this.nacosClientService = nacosClientService;
        this.serviceIds = serviceIds;
    }

    /**
     * 要保护的方法调用写在这里
     */
    @Override
    protected Observable<List<ServiceInstance>> construct() {
        return Observable.create(new Observable.OnSubscribe<List<ServiceInstance>>() {
            // Observable 有三个关键的事件方法, 分别是 onNext、onCompleted、onError
            @Override
            public void call(Subscriber<? super List<ServiceInstance>> subscriber) {

                try {
                    if (!subscriber.isUnsubscribed()) {
                        log.info("subscriber command task: [{}], [{}]",
                                JSON.toJSONString(serviceIds),
                                Thread.currentThread().getName());
                        serviceIds.forEach(
                                s -> subscriber
                                        .onNext(nacosClientService.getNacosClientInfo(s))
                        );
                        subscriber.onCompleted();
                        log.info("command task completed: [{}], [{}]",
                                JSON.toJSONString(serviceIds),
                                Thread.currentThread().getName());
                    }
                } catch (Exception ex) {
                    subscriber.onError(ex);
                }
            }
        });
    }

    /**
     * 服务降级
     */
    @Override
    protected Observable<List<ServiceInstance>> resumeWithFallback() {
        return Observable.create(new Observable.OnSubscribe<List<ServiceInstance>>() {
            @Override
            public void call(Subscriber<? super List<ServiceInstance>> subscriber) {

                try {
                    if (!subscriber.isUnsubscribed()) {
                        log.info("(fallback) subscriber command task: [{}], [{}]",
                                JSON.toJSONString(serviceIds),
                                Thread.currentThread().getName());
                        subscriber.onNext(Collections.emptyList());
                        subscriber.onCompleted();
                        log.info("(fallback) command task completed: [{}], [{}]",
                                JSON.toJSONString(serviceIds),
                                Thread.currentThread().getName());
                    }
                } catch (Exception ex) {
                    subscriber.onError(ex);
                }
            }
        });
    }
}

12.6.1.2 控制层开放 api

@GetMapping("/hystrix-observable-command")
public List<ServiceInstance> getServiceInstancesByServiceIdObservable(
        @RequestParam String serviceId) {

    List<String> serviceIds = Arrays.asList(serviceId, serviceId, serviceId);
    List<List<ServiceInstance>> result = new ArrayList<>(serviceIds.size());

    NacosClientHystrixObservableCommand observableCommand =
            new NacosClientHystrixObservableCommand(nacosClientService, serviceIds);

    // 异步执行命令
    Observable<List<ServiceInstance>> observe = observableCommand.observe();

    // 注册获取结果
    observe.subscribe(
            new Observer<List<ServiceInstance>>() {

                // 执行 onNext 之后再去执行 onCompleted
                @Override
                public void onCompleted() {
                    log.info("all tasks is complete: [{}], [{}]",
                            serviceId, Thread.currentThread().getName());
                }

                @Override
                public void onError(Throwable e) {
                    e.printStackTrace();
                }

                @Override
                public void onNext(List<ServiceInstance> instances) {
                    result.add(instances);
                }
            }
    );

    log.info("observable command result is : [{}], [{}]",
            JSON.toJSONString(result), Thread.currentThread().getName());
    return result.get(0);
}

12.6.1.3 hystrix.http

### 根据提供的 serviceId 获取实例信息
GET http://127.0.0.1:8000/scacommerce-nacos-client/hystrix/hystrix-observable-command?serviceId=sca-commerce-nacos-client
Content-Type: application/json

参考文章 - Hystrix 中线程池隔离与信号量隔离区别

12.7.1 开启Hystrix请求缓存

  • 先把请求缓存的思想搞清楚
    • Hystrix 的结果缓存指的是在一次 Hystrix 的请求上下文
  • Hystrix 请求缓存的两种实现方式
    • 编程(继承 HystrixCommand)方式,重写 getCacheKey 方法即可
    • 注解方式

12.7.1.1 编程方式

初始化 Hystrix 请求上下文环境

package com.edcode.commerce.filter;

import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategyDefault;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;
import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier;
import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;
import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher;
import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

/**
 * @author eddie.lee
 * @blog blog.eddilee.cn
 * @description 初始化 Hystrix 请求上下文环境
 */
@Slf4j
@Component
@WebFilter(filterName = "HystrixRequestContextServletFilter", urlPatterns = "/*", asyncSupported = true)
public class HystrixRequestContextServletFilter implements Filter {

	@Override
	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 初始化 Hystrix 请求上下文
        // 在不同的 context 中缓存是不共享的
        // 这个初始化是必须的
        HystrixRequestContext context = HystrixRequestContext.initializeContext();
        try {
            // 配置
            hystrixConcurrencyStrategyConfig();
            // 请求正常通过
            filterChain.doFilter(servletRequest, servletResponse);
        } finally {
            // 关闭 Hystrix 请求上下文
            context.shutdown();
        }
	}

	/**
	 * 配置 Hystrix 的并发策略
	 *
	 * 	为了解决 sleuth 问题
	 */
	public void hystrixConcurrencyStrategyConfig() {
		try {
			HystrixConcurrencyStrategy target = HystrixConcurrencyStrategyDefault.getInstance();
			HystrixConcurrencyStrategy strategy = HystrixPlugins.getInstance().getConcurrencyStrategy();
			if (strategy instanceof HystrixConcurrencyStrategyDefault) {
				// 如果已经就是我们想要配置的
				return;
			}
			// 将原来其他的配置保存下来
			HystrixCommandExecutionHook commandExecutionHook = HystrixPlugins.getInstance().getCommandExecutionHook();
			HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance().getEventNotifier();
			HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance().getMetricsPublisher();
			HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance().getPropertiesStrategy();

			// 先重置, 再把我们自定义的配置与原来的配置写回去
			HystrixPlugins.reset();
			HystrixPlugins.getInstance().registerConcurrencyStrategy(target);
			HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook);
			HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);
			HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);
			HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);

			log.info("配置hystrix并发策略成功");
		} catch (Exception ex) {
			log.error("未能注册Hystrix并发策略: [{}]", ex.getMessage(), ex);
		}
	}

}

扫描到 HystrixRequestContextServletFilter 过滤器

package com.edcode.commerce;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * @author eddie.lee
 * @blog blog.eddilee.cn
 * @description Nacos Client 工程启动入口
 */
@ServletComponentScan // 扫描到 HystrixRequestContextServletFilter 过滤器
@RefreshScope
@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
@EnableCircuitBreaker // 启动 hystrix
public class NacosClientApplication {

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

}

带有缓存功能的 Hystrix

package com.edcode.commerce.service.hystrix;

import com.edcode.commerce.service.NacosClientService;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixRequestCache;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategyDefault;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;

import java.util.Collections;
import java.util.List;

/**
 * @author eddie.lee
 * @blog blog.eddilee.cn
 * @description 带有缓存功能的 Hystrix
 */
@Slf4j
public class CacheHystrixCommand extends HystrixCommand<List<ServiceInstance>> {

	/** 需要保护的服务 */
	private final NacosClientService nacosClientService;

	/** 方法需要传递的参数 */
	private final String serviceId;

	private static final HystrixCommandKey CACHED_KEY = HystrixCommandKey.Factory.asKey("CacheHystrixCommand");

	public CacheHystrixCommand(NacosClientService nacosClientService, String serviceId) {

        super(
                HystrixCommand.Setter
                        .withGroupKey(HystrixCommandGroupKey
                                .Factory.asKey("CacheHystrixCommandGroup"))
                        .andCommandKey(CACHED_KEY)
        );

		this.nacosClientService = nacosClientService;
		this.serviceId = serviceId;
	}

	@Override
	protected List<ServiceInstance> run() throws Exception {

		log.info("获取服务实例的Hystrix命令中的CacheHystrixCommand:" + " [{}], [{}]",
                this.serviceId,
				Thread.currentThread().getName());
		return this.nacosClientService.getNacosClientInfo(this.serviceId);
	}

	@Override
	protected String getCacheKey() {
		return serviceId;
	}

	@Override
	protected List<ServiceInstance> getFallback() {
	    log.warn("com.edcode.commerce.service.hystrix.CacheHystrixCommand.getFallback: [{}]", Collections.emptyList());
		return Collections.emptyList();
	}

	/**
	 * 根据缓存 key 清理在一次 Hystrix 请求上下文中的缓存
	 */
	public static void flushRequestCache(String serviceId) {
		HystrixRequestCache.getInstance(
		        CACHED_KEY,
                HystrixConcurrencyStrategyDefault.getInstance()
        ).clear(serviceId);
		log.info("刷新hystrix命令中的请求缓存: [{}], [{}]",
                serviceId,
                Thread.currentThread().getName());
	}
}

确认一下 NacosClientService 是否正常

package com.edcode.commerce.service;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author eddie.lee
 * @blog blog.eddilee.cn
 * @description
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class NacosClientService {

    private final DiscoveryClient discoveryClient;

    /**
     * 打印 Nacos Client 信息到日志
     * @param serviceId
     * @return
     */
    public List<ServiceInstance> getNacosClientInfo(String serviceId) {

        // UseHystrixCommandAnnotation 测试超时
//        try {
//            Thread.sleep(2000);
//        } catch (InterruptedException e) {
//            //
//        }

        // NacosClientHystrixCommand 测试熔断
//        throw new RuntimeException("has some error");

        log.info("请求nacos客户端获取服务实例信息: [{}]", serviceId);
        return discoveryClient.getInstances(serviceId);
    }

}

HystrixController API

    @GetMapping("/cache-hystrix-command")
    public void cacheHystrixCommand(@RequestParam String serviceId) {

        // 使用缓存 Command, 发起两次请求
        CacheHystrixCommand command1 = new CacheHystrixCommand(
                nacosClientService, serviceId
        );
        CacheHystrixCommand command2 = new CacheHystrixCommand(
                nacosClientService, serviceId
        );

        List<ServiceInstance> result01 = command1.execute();
        List<ServiceInstance> result02 = command2.execute();
        log.info("result01, result02: [{}], [{}]",
                JSON.toJSONString(result01), JSON.toJSONString(result02));

        // 清除缓存
        CacheHystrixCommand.flushRequestCache(serviceId);

        // 使用缓存 Command, 发起两次请求
        CacheHystrixCommand command3 = new CacheHystrixCommand(
                nacosClientService, serviceId
        );
        CacheHystrixCommand command4 = new CacheHystrixCommand(
                nacosClientService, serviceId
        );

        List<ServiceInstance> result03 = command3.execute();
        List<ServiceInstance> result04 = command4.execute();
        log.info("result03, result04: [{}], [{}]",
                JSON.toJSONString(result03), JSON.toJSONString(result04));
    }

hystrix.http 请求测试

### 根据提供的 serviceId 获取实例信息
GET http://127.0.0.1:8000/scacommerce-nacos-client/hystrix/cache-hystrix-command?serviceId=sca-commerce-nacos-client
Content-Type: application/json
2021-12-07 21:54:30.963  INFO [sca-commerce-nacos-client,d14436a36d0d3e80,d14436a36d0d3e80,true] 4316 --- [nio-8000-exec-1] e.c.f.HystrixRequestContextServletFilter : 配置hystrix并发策略成功
2021-12-07 21:54:38.543  INFO [sca-commerce-nacos-client,,,] 4316 --- [xCommandGroup-1] c.e.c.s.hystrix.CacheHystrixCommand      : 获取服务实例的Hystrix命令中的CacheHystrixCommand: [sca-commerce-nacos-client], [hystrix-CacheHystrixCommandGroup-1]
2021-12-07 21:54:38.543  INFO [sca-commerce-nacos-client,,,] 4316 --- [xCommandGroup-1] c.e.commerce.service.NacosClientService  : 请求nacos客户端获取服务实例信息: [sca-commerce-nacos-client]
2021-12-07 21:54:56.293  INFO [sca-commerce-nacos-client,d14436a36d0d3e80,d14436a36d0d3e80,true] 4316 --- [nio-8000-exec-1] c.e.c.controller.HystrixController       : result01, result02: [[{"host":"192.168.3.192","metadata":{"nacos.instanceId":"192.168.3.192#8000#DEFAULT#DEFAULT_GROUP@@sca-commerce-nacos-client","nacos.weight":"1.0","nacos.cluster":"DEFAULT","nacos.ephemeral":"true","nacos.healthy":"true","preserved.register.source":"SPRING_CLOUD","management.context-path":"/scacommerce-nacos-client/actuator"},"port":8000,"secure":false,"serviceId":"sca-commerce-nacos-client","uri":"http://192.168.3.192:8000"}]], [[{"host":"192.168.3.192","metadata":{"nacos.instanceId":"192.168.3.192#8000#DEFAULT#DEFAULT_GROUP@@sca-commerce-nacos-client","nacos.weight":"1.0","nacos.cluster":"DEFAULT","nacos.ephemeral":"true","nacos.healthy":"true","preserved.register.source":"SPRING_CLOUD","management.context-path":"/scacommerce-nacos-client/actuator"},"port":8000,"secure":false,"serviceId":"sca-commerce-nacos-client","uri":"http://192.168.3.192:8000"}]]
2021-12-07 21:54:59.082  INFO [sca-commerce-nacos-client,d14436a36d0d3e80,d14436a36d0d3e80,true] 4316 --- [nio-8000-exec-1] c.e.c.s.hystrix.CacheHystrixCommand      : 刷新hystrix命令中的请求缓存: [sca-commerce-nacos-client], [http-nio-8000-exec-1]
2021-12-07 21:54:59.083  INFO [sca-commerce-nacos-client,,,] 4316 --- [xCommandGroup-2] c.e.c.s.hystrix.CacheHystrixCommand      : 获取服务实例的Hystrix命令中的CacheHystrixCommand: [sca-commerce-nacos-client], [hystrix-CacheHystrixCommandGroup-2]
2021-12-07 21:54:59.084  INFO [sca-commerce-nacos-client,,,] 4316 --- [xCommandGroup-2] c.e.commerce.service.NacosClientService  : 请求nacos客户端获取服务实例信息: [sca-commerce-nacos-client]
2021-12-07 21:54:59.086  INFO [sca-commerce-nacos-client,d14436a36d0d3e80,d14436a36d0d3e80,true] 4316 --- [nio-8000-exec-1] c.e.c.controller.HystrixController       : result03, result04: [[{"host":"192.168.3.192","metadata":{"nacos.instanceId":"192.168.3.192#8000#DEFAULT#DEFAULT_GROUP@@sca-commerce-nacos-client","nacos.weight":"1.0","nacos.cluster":"DEFAULT","nacos.ephemeral":"true","nacos.healthy":"true","preserved.register.source":"SPRING_CLOUD","management.context-path":"/scacommerce-nacos-client/actuator"},"port":8000,"secure":false,"serviceId":"sca-commerce-nacos-client","uri":"http://192.168.3.192:8000"}]], [[{"host":"192.168.3.192","metadata":{"nacos.instanceId":"192.168.3.192#8000#DEFAULT#DEFAULT_GROUP@@sca-commerce-nacos-client","nacos.weight":"1.0","nacos.cluster":"DEFAULT","nacos.ephemeral":"true","nacos.healthy":"true","preserved.register.source":"SPRING_CLOUD","management.context-path":"/scacommerce-nacos-client/actuator"},"port":8000,"secure":false,"serviceId":"sca-commerce-nacos-client","uri":"http://192.168.3.192:8000"}]]

12.7.1.2 注解方式

使用注解方式开启 Hystrix 请求缓存

package com.edcode.commerce.service.hystrix;

import com.edcode.commerce.service.NacosClientService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheKey;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheRemove;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author eddie.lee
 * @blog blog.eddilee.cn
 * @description 使用注解方式开启 Hystrix 请求缓存
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class CacheHystrixCommandAnnotation {

    private final NacosClientService nacosClientService;


    // 第一种 Hystrix Cache 注解的使用方法 (通常很少会这样写)
    @CacheResult(cacheKeyMethod = "getCacheKey")
    @HystrixCommand(commandKey = "CacheHystrixCommandAnnotation")
    public List<ServiceInstance> useCacheByAnnotation01(String serviceId) {
        log.info("使用 cache01 获取nacos客户端信息: [{}]", serviceId);
        return nacosClientService.getNacosClientInfo(serviceId);
    }

    @CacheRemove(commandKey = "CacheHystrixCommandAnnotation",
            cacheKeyMethod = "getCacheKey")
    @HystrixCommand
    public void flushCacheByAnnotation01(String cacheId) {
        log.info("刷新hystrix缓存密钥: [{}]", cacheId);
    }

    public String getCacheKey(String cacheId) {
        return cacheId;
    }

    // 第二种 Hystrix Cache 注解的使用方法 (推荐:上面的升级版,可以直观简单的知道 CacheKey)
    @CacheResult
    @HystrixCommand(commandKey = "CacheHystrixCommandAnnotation")
    public List<ServiceInstance> useCacheByAnnotation02(@CacheKey String serviceId) {

        log.info("使用 cache02 获取nacos客户端信息: [{}]", serviceId);
        return nacosClientService.getNacosClientInfo(serviceId);
    }

    @CacheRemove(commandKey = "CacheHystrixCommandAnnotation")
    @HystrixCommand
    public void flushCacheByAnnotation02(@CacheKey String cacheId) {
        log.info("刷新hystrix缓存密钥: [{}]", cacheId);
    }

    // 第三种 Hystrix Cache 注解的使用方法 (不推荐使用)
    @CacheResult
    @HystrixCommand(commandKey = "CacheHystrixCommandAnnotation")
    public List<ServiceInstance> useCacheByAnnotation03(String serviceId) {

        log.info("使用 cache03 获取nacos客户端信息: [{}]", serviceId);
        return nacosClientService.getNacosClientInfo(serviceId);
    }

    @CacheRemove(commandKey = "CacheHystrixCommandAnnotation")
    @HystrixCommand
    public void flushCacheByAnnotation03(String cacheId) {
        log.info("刷新hystrix缓存密钥: [{}]", cacheId);
    }

}

HystrixController API

    @GetMapping("/cache-annotation-01")
    public List<ServiceInstance> useCacheByAnnotation01(@RequestParam String serviceId) {

        log.info("使用 cache by annotation01(控制器)获取nacos客户端信息: [{}]", serviceId);

        List<ServiceInstance> result01 = cacheHystrixCommandAnnotation.useCacheByAnnotation01(serviceId);
        List<ServiceInstance> result02 = cacheHystrixCommandAnnotation.useCacheByAnnotation01(serviceId);

        // 清除掉缓存
        cacheHystrixCommandAnnotation.flushCacheByAnnotation01(serviceId);

        List<ServiceInstance> result03 = cacheHystrixCommandAnnotation.useCacheByAnnotation01(serviceId);
        // 这里有第四次调用
        return cacheHystrixCommandAnnotation.useCacheByAnnotation01(serviceId);
    }

    @GetMapping("/cache-annotation-02")
    public List<ServiceInstance> useCacheByAnnotation02(@RequestParam String serviceId) {

        log.info("使用 cache by annotation02(控制器)获取nacos客户端信息: [{}]", serviceId);

        List<ServiceInstance> result01 = cacheHystrixCommandAnnotation.useCacheByAnnotation02(serviceId);
        List<ServiceInstance> result02 = cacheHystrixCommandAnnotation.useCacheByAnnotation02(serviceId);

        // 清除掉缓存
        cacheHystrixCommandAnnotation.flushCacheByAnnotation02(serviceId);

        List<ServiceInstance> result03 = cacheHystrixCommandAnnotation.useCacheByAnnotation02(serviceId);
        // 这里有第四次调用
        return cacheHystrixCommandAnnotation.useCacheByAnnotation02(serviceId);
    }

    @GetMapping("/cache-annotation-03")
    public List<ServiceInstance> useCacheByAnnotation03(@RequestParam String serviceId) {

        log.info("使用 cache by annotation03(控制器)获取nacos客户端信息: [{}]", serviceId);

        List<ServiceInstance> result01 = cacheHystrixCommandAnnotation.useCacheByAnnotation03(serviceId);
        List<ServiceInstance> result02 = cacheHystrixCommandAnnotation.useCacheByAnnotation03(serviceId);

        // 清除掉缓存
        cacheHystrixCommandAnnotation.flushCacheByAnnotation03(serviceId);

        List<ServiceInstance> result03 = cacheHystrixCommandAnnotation.useCacheByAnnotation03(serviceId);
        // 这里有第四次调用
        return cacheHystrixCommandAnnotation.useCacheByAnnotation03(serviceId);
    }
   

hystrix.http 请求测试

### 根据提供的 serviceId 获取实例信息
GET http://127.0.0.1:8000/scacommerce-nacos-client/hystrix/cache-annotation-01?serviceId=sca-commerce-nacos-client
Content-Type: application/json

### 根据提供的 serviceId 获取实例信息
GET http://127.0.0.1:8000/scacommerce-nacos-client/hystrix/cache-annotation-02?serviceId=sca-commerce-nacos-client
Content-Type: application/json

### 根据提供的 serviceId 获取实例信息
GET http://127.0.0.1:8000/scacommerce-nacos-client/hystrix/cache-annotation-03?serviceId=sca-commerce-nacos-client
Content-Type: application/json

抽取 01 记录讲解

2021-12-07 22:35:22.544  INFO [sca-commerce-nacos-client,86bbcdc91ff974b1,86bbcdc91ff974b1,true] 15812 --- [nio-8000-exec-3] c.e.c.controller.HystrixController       : 使用 cache by annotation01(控制器)获取nacos客户端信息: [sca-commerce-nacos-client]
2021-12-07 22:35:22.547  INFO [sca-commerce-nacos-client,,,] 15812 --- [andAnnotation-2] c.e.c.s.h.CacheHystrixCommandAnnotation  : 使用 cache01 获取nacos客户端信息: [sca-commerce-nacos-client]
2021-12-07 22:35:22.547  INFO [sca-commerce-nacos-client,,,] 15812 --- [andAnnotation-2] c.e.commerce.service.NacosClientService  : 请求nacos客户端获取服务实例信息: [sca-commerce-nacos-client]
2021-12-07 22:35:22.563  INFO [sca-commerce-nacos-client,,,] 15812 --- [andAnnotation-3] c.e.c.s.h.CacheHystrixCommandAnnotation  : 刷新hystrix缓存密钥: [sca-commerce-nacos-client]
2021-12-07 22:35:22.567  INFO [sca-commerce-nacos-client,,,] 15812 --- [andAnnotation-4] c.e.c.s.h.CacheHystrixCommandAnnotation  : 使用 cache01 获取nacos客户端信息: [sca-commerce-nacos-client]
2021-12-07 22:35:22.567  INFO [sca-commerce-nacos-client,,,] 15812 --- [andAnnotation-4] c.e.commerce.service.NacosClientService  : 请求nacos客户端获取服务实例信息: [sca-commerce-nacos-client]

在“刷新hystrix缓存密钥”之前,只出现过一次“使用 cache01 获取nacos客户端信息”而没有出现两次这样打印,是因为已经缓存了,所以没有出现 result02 的打印,至于“刷新hystrix缓存密钥”之后,是因为 result03 已经在缓存里面,return 是第四次请求,所以在打印时候,只是打印了 result03 的信息。

12.10.1 Hystrix请求合并

  • 请求合并的思想
    • 默认情况下,每一个请求都会占用一个线程和一次网络请求,高并发场景下效率不高
    • 使用 Hystrix 的请求合并,将多个请求 merge 为一个,提高服务的并发能力

在这里插入图片描述

最终 就是三次请求,合并成一次请求(在某个时间内)

12.10.1.1 编程方式示例代码

NacosClientService 提供给编程方式的 Hystrix 请求合并

public List<List<ServiceInstance>> getNacosClientInfos(List<String> serviceIds) {

    log.info("请求nacos客户端获取服务实例信息: [{}]", JSON.toJSONString(serviceIds));
    List<List<ServiceInstance>> result = new ArrayList<>(serviceIds.size());

    serviceIds.forEach(s -> result.add(discoveryClient.getInstances(s)));
    return result;
}

批量请求 Hystrix Command

package com.edcode.commerce.service.hystrix.request_merge;

import com.alibaba.fastjson.JSON;
import com.edcode.commerce.service.NacosClientService;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;

import java.util.Collections;
import java.util.List;

/**
 * @author eddie.lee
 * @blog blog.eddilee.cn
 * @description 批量请求 Hystrix Command
 */
@Slf4j
public class NacosClientBatchCommand extends HystrixCommand<List<List<ServiceInstance>>> {

    private final NacosClientService nacosClientService;

    private final List<String> serviceIds;

    protected NacosClientBatchCommand(NacosClientService nacosClientService, List<String> serviceIds) {

        super(
                HystrixCommand.Setter.withGroupKey(
                        HystrixCommandGroupKey.Factory.asKey("NacosClientBatchCommand")
                )
        );

        this.nacosClientService = nacosClientService;
        this.serviceIds = serviceIds;
    }

    @Override
    protected List<List<ServiceInstance>> run() throws Exception {
        log.info("使用nacos客户端批处理命令获取结果: [{}]", JSON.toJSONString(serviceIds));
        return nacosClientService.getNacosClientInfos(serviceIds);
    }

    @Override
    protected List<List<ServiceInstance>> getFallback() {
        log.warn("nacos客户端批处理命令失败,请使用回退!");
        return Collections.emptyList();
    }
}

请求合并器 - HystrixCollapser

package com.edcode.commerce.service.hystrix.request_merge;

import com.edcode.commerce.service.NacosClientService;
import com.netflix.hystrix.HystrixCollapser;
import com.netflix.hystrix.HystrixCollapserKey;
import com.netflix.hystrix.HystrixCollapserProperties;
import com.netflix.hystrix.HystrixCommand;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author eddie.lee
 * @blog blog.eddilee.cn
 * @description 请求合并器
 */
@Slf4j
public class NacosClientCollapseCommand extends HystrixCollapser<List<List<ServiceInstance>>, List<ServiceInstance>, String> {

    private final NacosClientService nacosClientService;
    
    private final String serviceId;

    public NacosClientCollapseCommand(NacosClientService nacosClientService, String serviceId) {

        super(
                HystrixCollapser.Setter.withCollapserKey(
                        HystrixCollapserKey.Factory.asKey("NacosClientCollapseCommand")
                ).andCollapserPropertiesDefaults(
                        // 等待 300毫秒 的请求都合并,之后的重新等待
                        HystrixCollapserProperties.Setter().withTimerDelayInMilliseconds(300)
                )
        );

        this.nacosClientService = nacosClientService;
        this.serviceId = serviceId;
    }

	/**
	 * 获取请求中的参数
	 */
    @Override
    public String getRequestArgument() {
        return this.serviceId;
    }

	/**
	 * 创建批量请求 Hystrix Command
	 */
    @Override
    protected HystrixCommand<List<List<ServiceInstance>>> createCommand(Collection<CollapsedRequest<List<ServiceInstance>, String>> collapsedRequests) {
        List<String> serviceIds = new ArrayList<>(collapsedRequests.size());
        serviceIds.addAll(
                collapsedRequests.stream()
                        .map(CollapsedRequest::getArgument)
                        .collect(Collectors.toList())
        );
        return new NacosClientBatchCommand(nacosClientService, serviceIds);
    }

	/**
	 * 响应分发给单独的请求
	 */
    @Override
    protected void mapResponseToRequests(List<List<ServiceInstance>> batchResponse, Collection<CollapsedRequest<List<ServiceInstance>, String>> collapsedRequests) {
        int count = 0;
        for (CollapsedRequest<List<ServiceInstance>, String> collapsedRequest : collapsedRequests) {
            // 从批量响应集合中按顺序取出结果
            List<ServiceInstance> instances = batchResponse.get(count++);
            // 将结果返回原 Response 中
            collapsedRequest.setResponse(instances);
        }
    }
}

编程方式实现请求合并 - API

    @GetMapping("/request-merge")
    public void requestMerge() throws Exception {

        // 前三个请求会被合并
        NacosClientCollapseCommand collapseCommand01 = new NacosClientCollapseCommand(nacosClientService, "sca-commerce-nacos-client1");
        NacosClientCollapseCommand collapseCommand02 = new NacosClientCollapseCommand(nacosClientService, "sca-commerce-nacos-client2");
        NacosClientCollapseCommand collapseCommand03 = new NacosClientCollapseCommand(nacosClientService, "sca-commerce-nacos-client3");

        // 异步非阻塞获取结果
        Future<List<ServiceInstance>> future01 = collapseCommand01.queue();
        Future<List<ServiceInstance>> future02 = collapseCommand02.queue();
        Future<List<ServiceInstance>> future03 = collapseCommand03.queue();

        future01.get();
        future02.get();
        future03.get();

        log.info("睡眠开始");
        Thread.sleep(2000);
        log.info("睡眠结束");

        // 过了合并的时间窗口, 第四个请求单独发起
        NacosClientCollapseCommand collapseCommand04 = new NacosClientCollapseCommand(nacosClientService, "sca-commerce-nacos-client4");
        Future<List<ServiceInstance>> future04 = collapseCommand04.queue();
        future04.get();
    }

hystrix.http

### 根据提供的 serviceId 获取实例信息
GET http://127.0.0.1:8000/scacommerce-nacos-client/hystrix/request-merge
Content-Type: application/json

日志

2021-12-08 23:20:02.109  INFO [sca-commerce-nacos-client,,,] 13236 --- [tBatchCommand-2] c.e.c.s.h.r.NacosClientBatchCommand      : 使用nacos客户端批处理命令获取结果: [["sca-commerce-nacos-client1","sca-commerce-nacos-client3","sca-commerce-nacos-client2"]]
2021-12-08 23:20:02.109  INFO [sca-commerce-nacos-client,,,] 13236 --- [tBatchCommand-2] c.e.commerce.service.NacosClientService  : 请求nacos客户端获取服务实例信息: [["sca-commerce-nacos-client1","sca-commerce-nacos-client3","sca-commerce-nacos-client2"]]
2021-12-08 23:20:02.112  INFO [sca-commerce-nacos-client,5e186d19164c898b,5e186d19164c898b,true] 13236 --- [nio-8000-exec-4] c.e.c.controller.HystrixController       : 睡眠开始
2021-12-08 23:20:04.119  INFO [sca-commerce-nacos-client,5e186d19164c898b,5e186d19164c898b,true] 13236 --- [nio-8000-exec-4] c.e.c.controller.HystrixController       : 睡眠结束
2021-12-08 23:20:04.213  INFO [sca-commerce-nacos-client,,,] 13236 --- [tBatchCommand-3] c.e.c.s.h.r.NacosClientBatchCommand      : 使用nacos客户端批处理命令获取结果: [["sca-commerce-nacos-client4"]]
2021-12-08 23:20:04.214  INFO [sca-commerce-nacos-client,,,] 13236 --- [tBatchCommand-3] c.e.commerce.service.NacosClientService  : 请求nacos客户端获取服务实例信息: [["sca-commerce-nacos-client4"]]

12.10.1.2 适用场景与注意事项

  • 适用场景
    • 单个对象的查询并发数很高,服务提供方负载较高,就可以考虑使用请求合并
  • 注意事项
    • 请求在代码中人未的设置了延迟时间,会降低请求的响应速度
    • 可能会提高服务提供方的负载,因为返回 List 结果数据量量变大
    • 实现请求合并比较复杂

12.10.1.3 注解方式应用Hystrix请求合并(一般开发都使用,简单粗暴)

NacosClientService

// 使用注解实现 Hystrix 请求合并
@HystrixCollapser(
        // 批量的方法名称
        batchMethod = "findNacosClientInfos",
        // 全局合并:所有的接口进来都合并
        scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL,
        collapserProperties = {
                // 300毫秒
                @HystrixProperty(name = "timerDelayInMilliseconds", value = "300")
        }
)
public Future<List<ServiceInstance>> findNacosClientInfo(String serviceId) {
    // 系统运行正常, 不会走这个方法
    throw new RuntimeException("不应执行此方法体!");
}

@HystrixCommand
public List<List<ServiceInstance>> findNacosClientInfos(List<String> serviceIds) {
    log.info("进入查找nacos客户端信息: [{}]", JSON.toJSONString(serviceIds));
    return getNacosClientInfos(serviceIds);
}

HystrixController

/**
 * 注解的方式实现请求合并
 */
@GetMapping("/request-merge-annotation")
public void requestMergeAnnotation() throws Exception {

    Future<List<ServiceInstance>> future01 = nacosClientService.findNacosClientInfo(
            "sca-commerce-nacos-client1"
    );
    Future<List<ServiceInstance>> future02 = nacosClientService.findNacosClientInfo(
            "sca-commerce-nacos-client2"
    );
    Future<List<ServiceInstance>> future03 = nacosClientService.findNacosClientInfo(
            "sca-commerce-nacos-client3"
    );

    future01.get();
    future02.get();
    future03.get();

    log.info("睡眠开始");
    Thread.sleep(2000);
    log.info("睡眠结束");

    Future<List<ServiceInstance>> future04 = nacosClientService.findNacosClientInfo(
            "sca-commerce-nacos-client4"
    );
    future04.get();
}

hystrix.http

### 根据提供的 serviceId 获取实例信息
GET http://127.0.0.1:8000/scacommerce-nacos-client/hystrix/request-merge-annotation
Content-Type: application/json

在这里插入图片描述

12.13.1 OpenFeign集成Hystrix开启后备模式(兜底策略)

  • OpenFeign 集成 Hystrix 的步骤
    • 在配置文件中开启 Hystrix 的熔断功能:feign.hystrix.enabled:true
    • @FeignClient 注解的 fallback 和 fallbackFactory 属性
      • fallback 和 fallbackFactory 属性 都是用于配置响应的回退,但是不可以同时使用
      • fallbackFactory 能够获取到 OpenFeign 调用抛出的异常

12.13.1.1 Fallback 介绍与代码示例

@FeignClient 注解里面的 fallback() 方法

/**
 * 指定的外部客户端接口的回退类。后备类必须
 * 实现由该注释注释的接口,并成为有效的Springbean。
 * @return 指定的外部客户端接口的回退类
 */
Class<?> fallback() default void.class;

bootstrap.yml 开启 OpenFeign 集成 Hystrix 开启后备模式

feign:
  hystrix:
    enabled: true

AuthorityFeignClient 后备 fallback (兜底方法)

package com.edcode.commerce.service.communication.hystrix;

import com.alibaba.fastjson.JSON;
import com.edcode.commerce.service.communication.feign.AuthorityFeignClient;
import com.edcode.commerce.vo.JwtToken;
import com.edcode.commerce.vo.UsernameAndPassword;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * @author eddie.lee
 * @blog blog.eddilee.cn
 * @description AuthorityFeignClient 后备 fallback
 */
@Slf4j
@Component
public class AuthorityFeignClientFallback implements AuthorityFeignClient {

    @Override
    public JwtToken getTokenByFeign(UsernameAndPassword usernameAndPassword) {
        log.info("授权外部客户端通过外部请求获取令牌错误 " + "(Hystrix Fallback):[{}]", JSON.toJSONString(usernameAndPassword));
        return new JwtToken("JwtToken Hystrix Fallback");
    }

}

AuthorityFeignClient 类里 @FeignClient 添加 fallback

@FeignClient(
		contextId = "AuthorityFeignClient",
		value = "sca-commerce-authority-center",
		fallback = AuthorityFeignClientFallback.class
)
public interface AuthorityFeignClient {

    /**
     * 通过 OpenFeign 访问 Authority 获取 Token
     * @param usernameAndPassword
     * @return
     */
	@RequestMapping(
			value = "/scacommerce-authority-center/authority/token",
			method = RequestMethod.POST, consumes = "application/json",
			produces = "application/json"
	)
	JwtToken getTokenByFeign(@RequestBody UsernameAndPassword usernameAndPassword);

}

在 communication.http 寻找 【通过 OpenFeign 获取 Token】

### 通过 OpenFeign 获取 Token
POST http://127.0.0.1:8000/scacommerce-nacos-client/communication/token-by-feign
Content-Type: application/json

{
  "username": "eddie@qq.com",
  "password": "25d55ad283aa400af464c76d713c07ad"
}

返回兜底信息

{
  "code": 0,
  "message": "",
  "data": {
    "token": "JwtToken Hystrix Fallback"
  }
}

AuthorityCenterApplication 不用启动,就启动 NacosClientApplication 服务即可,因为不启动授权服务,那么肯定会报错的

12.14.1.1 FallbackFactory 介绍与代码示例 (推荐)

OpenFeign 集成 Hystrix 的另外一种模式

package com.edcode.commerce.service.communication.hystrix;

import com.edcode.commerce.service.communication.feign.AuthorityFeignClient;
import com.edcode.commerce.vo.JwtToken;
import com.edcode.commerce.vo.UsernameAndPassword;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * @author eddie.lee
 * @blog blog.eddilee.cn
 * @description OpenFeign 集成 Hystrix 的另外一种模式
 */
@Slf4j
@Component
public class AuthorityFeignClientFallbackFactory implements FallbackFactory<AuthorityFeignClient> {

    @Override
    public AuthorityFeignClient create(Throwable throwable) {

        log.warn("授权外部客户端通过外部请求获取令牌错误 " + "(Hystrix FallbackFactory):[{}]", throwable.getMessage(),throwable);

        return new AuthorityFeignClient(){

            @Override
            public JwtToken getTokenByFeign(UsernameAndPassword usernameAndPassword) {
                return new JwtToken("JwtToken Hystrix FallbackFactory");
            }
        };
    }
}

与 Authority 服务通信的 Feign Client 接口定义

package com.edcode.commerce.service.communication.feign;

import com.edcode.commerce.service.communication.hystrix.AuthorityFeignClientFallback;
import com.edcode.commerce.service.communication.hystrix.AuthorityFeignClientFallbackFactory;
import com.edcode.commerce.vo.JwtToken;
import com.edcode.commerce.vo.UsernameAndPassword;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * @author eddie.lee
 * @blog blog.eddilee.cn
 * @description 与 Authority 服务通信的 Feign Client 接口定义
 */
@FeignClient(
		contextId = "AuthorityFeignClient",
		value = "sca-commerce-authority-center",
//		fallback = AuthorityFeignClientFallback.class // 不会返回错误信息与自定义提示
		fallbackFactory = AuthorityFeignClientFallbackFactory.class  // 会返回错误信息与自定义提示
)
public interface AuthorityFeignClient {

    /**
     * 通过 OpenFeign 访问 Authority 获取 Token
     * @param usernameAndPassword
     * @return
     */
	@RequestMapping(
			value = "/scacommerce-authority-center/authority/token",
			method = RequestMethod.POST, consumes = "application/json",
			produces = "application/json"
	)
	JwtToken getTokenByFeign(@RequestBody UsernameAndPassword usernameAndPassword);

}

在 communication.http 寻找 【通过 OpenFeign 获取 Token】

### 通过 OpenFeign 获取 Token
POST http://127.0.0.1:8000/scacommerce-nacos-client/communication/token-by-feign
Content-Type: application/json

{
  "username": "eddie@qq.com",
  "password": "25d55ad283aa400af464c76d713c07ad"
}

返回兜底信息

{
  "code": 0,
  "message": "",
  "data": {
    "token": "JwtToken Hystrix FallbackFactory"
  }
}

终端会显示错误显示

2021-12-13 19:57:49.994  WARN [sca-commerce-nacos-client,,,] 15724 --- [hority-center-2] .c.h.AuthorityFeignClientFallbackFactory : 授权外部客户端通过外部请求获取令牌错误 (Hystrix FallbackFactory):[com.netflix.client.ClientException: Load balancer does not have available server for client: sca-commerce-authority-center]

java.lang.RuntimeException: com.netflix.client.ClientException: Load balancer does not have available server for client: sca-commerce-authority-center
	at org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient.execute(LoadBalancerFeignClient.java:90) ~[spring-cloud-openfeign-core-2.2.5.RELEASE.jar:2.2.5.RELEASE]

...

12.15.1 使用Hystrix监控面板监测客户端容错

  • Hystrix Dashboard
    • Hystrix Dashboard 是一个单独的应用,用来实时的对 Hystrix 的【使用情况】进行实时的监控

12.15.1.1 创建独立的工程

项目结构图

在这里插入图片描述

在这里插入图片描述

Maven依赖
<?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>sca-commerce</artifactId>
        <groupId>com.edcode.commerce</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>sca-commerce-hystrix-dashboard</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <!-- 模块名及描述信息 -->
    <name>sca-commerce-hystrix-dashboard</name>
    <description>Hystrix Dashboard</description>

    <dependencies>
        <!-- spring cloud alibaba nacos discovery 依赖 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>2.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</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-netflix-hystrix-dashboard</artifactId>
        </dependency>
    </dependencies>

    <!--
        SpringBoot的Maven插件, 能够以Maven的方式为应用提供SpringBoot的支持,可以将
        SpringBoot应用打包为可执行的jar或war文件, 然后以通常的方式运行SpringBoot应用
     -->
    <build>
        <finalName>${artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>


</project>
bootstrap.yml
server:
  port: 9999
  servlet:
    context-path: /scacommerce-hystrix-dashboard

spring:
  application:
    name: sca-commerce-hystrix-dashboard
  cloud:
    nacos:
      # 服务注册发现
      discovery:
        enabled: true # 如果不想使用 Nacos 进行服务注册和发现, 设置为 false 即可
        #server-addr: ${NACOS_ADDR:127.0.0.1}:8848
        server-addr: ${NACOS_ADDR_1:127.0.0.1}:8848,${NACOS_ADDR_2:127.0.0.1}:8849,${NACOS_ADDR_3:127.0.0.1}:8850 # Nacos 服务器地址
        namespace: ${NAMESPACE_ID:1adcfdd8-5763-4768-9a15-9c7157988950}
        metadata:
          management:
            context-path: ${server.servlet.context-path}/actuator
  redis:
    database: 0
    host: ${REDIS_HOST:localhost}
    port: ${REDIS_PORT:6379}
    timeout: 5000

hystrix:
  dashboard:
    # 允许什么IP地址可以访问 hystrix-dashboard 监控
    proxy-stream-allow-list: "127.0.0.1,192.168.3.192"

# 暴露端点
management:
  endpoints:
    web:
      exposure:
        include: '*'
  endpoint:
    health:
      show-details: always
启动类
package com.edcode.commerce;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;

/**
 * @author eddie.lee
 * @blog blog.eddilee.cn
 * @description hystrix dashboard 入口
 *
 *  127.0.0.1:9999/scacommerce-hystrix-dashboard/hystrix/
 *  http://127.0.0.1:8000/scacommerce-hystrix-dashboard/actuator/hystrix.stream
 *
 */
@EnableDiscoveryClient
@SpringBootApplication
@EnableHystrixDashboard     // 开启 Hystrix Dashboard
public class HystrixDashboardApplication {

    public static void main(String[] args) {

        SpringApplication.run(HystrixDashboardApplication.class, args);
    }
}
测试请求

在 Gatway 请求 login.http 获取 token

### 登录
POST 127.0.0.1:9001/edcode/sca-commerce/login
Content-Type: application/json

{
  "username": "eddie@qq.com",
  "password": "25d55ad283aa400af464c76d713c07ad"
}

启动 NacosClientApplication、AuthorityCenterApplication、HystrixDashboardApplication、GatewayApplication 四个服务,获取 jwt token

sca-commerce-hystrix-dashboard 创建 dashboard.http 请求

### 获取 Token
POST http://127.0.0.1:9001/edcode/scacommerce-nacos-client/communication/token-by-feign
Content-Type: application/json
sca-commerce-user: eyJhbGciOiJSUzI1NiJ9.eyJzY2EtY29tbWVyY2UtdXNlciI6IntcImlkXCI6MTEsXCJ1c2VybmFtZVwiOlwiZWRkaWVAcXEuY29tXCJ9IiwianRpIjoiNjYzNWMzNzctNzU1OC00YTE3LTgwZDgtZGRkNjMwNDRlOWY3IiwiZXhwIjoxNjM5NDExMjAwfQ.YCuXyNFXSJBRQQPEYAn_k--IgsVcwG1yrkPIfEdPyaO3cs4gs4JhDiNSHtAOnI6Jm0-eedO6GZ9a0B85lT5mN6B1Oko9SYsIb5qPUvFHD3rcAlJoOF7uRqf6QSVismBCS-xY5ewiVRRHvfsEZ-Kksa9yOsHD5OfpuSMtSHZhXsRKUA-MZjfrhwMDfx6Vg-BfVbnvAJVK2vbxyzq2-rSnFeYrfd38QK6hmO_PJrB83Xht8CU8RTari0uys4GsudZnLciAV1pxRbx5Lpg95008ltezX9-AuamEsbnq0of7OwY8TfOlhcpMBYWLR8rE7Fda8djCFLSvblrpqFniT1480A
token: edcode

{
  "username": "eddie@qq.com",
  "password": "25d55ad283aa400af464c76d713c07ad"
}


### 根据提供的 serviceId 获取实例信息
GET http://127.0.0.1:9001/edcode/scacommerce-nacos-client/hystrix/hystrix-command-annotation?serviceId=e-commerce-nacos-client
Content-Type: application/json
sca-commerce-user: eyJhbGciOiJSUzI1NiJ9.eyJzY2EtY29tbWVyY2UtdXNlciI6IntcImlkXCI6MTEsXCJ1c2VybmFtZVwiOlwiZWRkaWVAcXEuY29tXCJ9IiwianRpIjoiNjYzNWMzNzctNzU1OC00YTE3LTgwZDgtZGRkNjMwNDRlOWY3IiwiZXhwIjoxNjM5NDExMjAwfQ.YCuXyNFXSJBRQQPEYAn_k--IgsVcwG1yrkPIfEdPyaO3cs4gs4JhDiNSHtAOnI6Jm0-eedO6GZ9a0B85lT5mN6B1Oko9SYsIb5qPUvFHD3rcAlJoOF7uRqf6QSVismBCS-xY5ewiVRRHvfsEZ-Kksa9yOsHD5OfpuSMtSHZhXsRKUA-MZjfrhwMDfx6Vg-BfVbnvAJVK2vbxyzq2-rSnFeYrfd38QK6hmO_PJrB83Xht8CU8RTari0uys4GsudZnLciAV1pxRbx5Lpg95008ltezX9-AuamEsbnq0of7OwY8TfOlhcpMBYWLR8rE7Fda8djCFLSvblrpqFniT1480A
token: edcode


### 根据提供的 serviceId 获取实例信息
GET http://127.0.0.1:9001/edcode/scacommerce-nacos-client/hystrix/simple-hystrix-command?serviceId=e-commerce-nacos-client
Content-Type: application/json
sca-commerce-user: eyJhbGciOiJSUzI1NiJ9.eyJzY2EtY29tbWVyY2UtdXNlciI6IntcImlkXCI6MTEsXCJ1c2VybmFtZVwiOlwiZWRkaWVAcXEuY29tXCJ9IiwianRpIjoiNjYzNWMzNzctNzU1OC00YTE3LTgwZDgtZGRkNjMwNDRlOWY3IiwiZXhwIjoxNjM5NDExMjAwfQ.YCuXyNFXSJBRQQPEYAn_k--IgsVcwG1yrkPIfEdPyaO3cs4gs4JhDiNSHtAOnI6Jm0-eedO6GZ9a0B85lT5mN6B1Oko9SYsIb5qPUvFHD3rcAlJoOF7uRqf6QSVismBCS-xY5ewiVRRHvfsEZ-Kksa9yOsHD5OfpuSMtSHZhXsRKUA-MZjfrhwMDfx6Vg-BfVbnvAJVK2vbxyzq2-rSnFeYrfd38QK6hmO_PJrB83Xht8CU8RTari0uys4GsudZnLciAV1pxRbx5Lpg95008ltezX9-AuamEsbnq0of7OwY8TfOlhcpMBYWLR8rE7Fda8djCFLSvblrpqFniT1480A
token: edcode


### 根据提供的 serviceId 获取实例信息
GET http://127.0.0.1:9001/edcode/scacommerce-nacos-client/hystrix/hystrix-observable-command?serviceId=e-commerce-nacos-client
Content-Type: application/json
sca-commerce-user: eyJhbGciOiJSUzI1NiJ9.eyJzY2EtY29tbWVyY2UtdXNlciI6IntcImlkXCI6MTEsXCJ1c2VybmFtZVwiOlwiZWRkaWVAcXEuY29tXCJ9IiwianRpIjoiNjYzNWMzNzctNzU1OC00YTE3LTgwZDgtZGRkNjMwNDRlOWY3IiwiZXhwIjoxNjM5NDExMjAwfQ.YCuXyNFXSJBRQQPEYAn_k--IgsVcwG1yrkPIfEdPyaO3cs4gs4JhDiNSHtAOnI6Jm0-eedO6GZ9a0B85lT5mN6B1Oko9SYsIb5qPUvFHD3rcAlJoOF7uRqf6QSVismBCS-xY5ewiVRRHvfsEZ-Kksa9yOsHD5OfpuSMtSHZhXsRKUA-MZjfrhwMDfx6Vg-BfVbnvAJVK2vbxyzq2-rSnFeYrfd38QK6hmO_PJrB83Xht8CU8RTari0uys4GsudZnLciAV1pxRbx5Lpg95008ltezX9-AuamEsbnq0of7OwY8TfOlhcpMBYWLR8rE7Fda8djCFLSvblrpqFniT1480A
token: edcode

###

测试请求失败的兜底策略,关闭【授权中心 - AuthorityCenterApplication】,然后请求【获取 Token】的http

在这里插入图片描述
在这里插入图片描述

Hystrix Dashboard 右上角 颜色对应翻译:成功|短路|请求错误|超时|被拒绝|失败|错误%,颜色对应接口的参数信息,比如上图的 1 是红色数字,对应是 Failure(失败)