springcloud动力节点-04Hystrix
Spring Cloud Hystrix
1.前言
1.1 什么是服务雪崩
1.2 服务雪崩怎么解决
1.2.1 修改调用的超时时长(不推荐)
1.2.2 设置拦截器
2.Spring Cloud Hystrix 简介
3.Hystrix 快速入门
3.1 在 OpenFeign 中使用 Hystrix(重点)
3.1.1 启动 provider-order-service: 8761/eureka
3.1.1.1 先创建 rent-car-service,选择依赖: web \ eureka client
3.1.1.2 rent-car-service 修改配置文件
# 应用服务 WEB 访问端口 server: port: 8080 spring: application: name: rent-car-service eureka: client: service-url: defaultZone: http://localhost:8761/eureka instance: hostname: localhost instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
server: # 源文档 port: 8082 spring: application: name: provider-order-service eureka: client: service-url: defaultZone: http://localhost:8761/eureka instance: instance-id: ${spring.application.name}:${server.port} prefer-ip-address: true
3.1.1.3 rent-car-service 修改启动类增加一个访问接口
package com.tongda.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class RentCarController { @GetMapping("rent") public String rent() { return "租车成功"; } }
3.1.1.4 rent-car-service 启动测试访问
3.1.2 新建 customer-service:Web \ eureka client \ Openfeign \ hystrix
添加依赖
<!-- 熔断机制:https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-hystrix --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>2.2.10.RELEASE</version> </dependency>
3.1.2.1 CustomerController
package com.tongda.controller; import com.tongda.feign.CustomerRentFeign; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class CustomerController { @Autowired private CustomerRentFeign customerRentFeign; @GetMapping("customerRent") public String CustomerRent(){ System.out.println("客户前来租车"); // 远程调用RPC String rent = customerRentFeign.rent(); return rent; } }
Feign中CustomerRentFeign接口
package com.tongda.feign; import com.tongda.feign.hystrix.CustomerRentFeignHystrix; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; // 有实现类备选方案时:fallback=类名.class // 需要指定熔断的类 @FeignClient(value = "rent-car-service",fallback = CustomerRentFeignHystrix.class) public interface CustomerRentFeign { // 调用ren-car-service @GetMapping("rent") public String rent(); }
hystrix接口实现:备选方案@FeignClient(value = "rent-car-service",fallback = CustomerRentFeignHystrix.class)
package com.tongda.feign.hystrix; import com.tongda.feign.CustomerRentFeign; import org.springframework.stereotype.Component; /** * hystrix熔断机制: * feign接口的实现类 * @Date 2023/7/26 10:06 * @Version 1.0 */ @Component // 配置类:组件,必须需要加入ioc容器 public class CustomerRentFeignHystrix implements CustomerRentFeign { @Override public String rent() { return "我是备用方案"; } }
3.1.2.2 修改 CustomerRentFeign接口 增加一个 fallback=指定类名.class
// 有实现类备选方案时:fallback=类名.class // 需要指定熔断的类 @FeignClient(value = "rent-car-service",fallback = CustomerRentFeignHystrix.class)
3.1.2.3 修改 yml 配置文件:Openfeign中配置hystrix熔断器时,circuitbreaker熔断器enabled:true开启
# 应用服务 WEB 访问端口 server: port: 8081 spring: application: name: customer-service eureka: client: service-url: defaultZone: http://localhost:8761/eureka instance: hostname: localhost instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port} feign: # Openfeign中开启hystrix circuitbreaker: # 注意 enabled: true # 在cloud的F版以前默认开启,但因有了其他的熔断组件,现默认关闭
3.1.4 关掉 rent-car-service 访问测试; 8080宕机时
说明 Hystrix 生效了
问题:@Autowired 报红原因
原因一个类型2个对象造成问题
1. 使用@Resourece, Spring bean名字区分
2. 使用@Autowired+@Qualifier("指定bean名字")
3. 使用下面方法,选择Syntax解决
4.手写断路器
4.1 断路器的设计
4.2 断路器的状态说明以及状态转变
4.3 开始设计断路器模型
4.3.1 创建项目选择依赖
4.3.2 创建断路器状态模型 HystrixStatus
package com.tongda.model; /** * 断路器状态开关: Open\Close\Half_Open半开 * 引用spring拦截器AOP,先导入依赖 * @Date 2023/7/28 11:27 * @Version 1.0 */ public enum FishStatus { CLOSE, OPEN, HALF_OPEN }
package com.tongda.model; import lombok.Data; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * 这个是断路器的模型 * * @Date 2023/7/29 11:43 * @Version 1.0 */ @Data public class Fish { // 断路器状态:默认是关闭 // 成员变量private private FishStatus status = FishStatus.CLOSE; // 断路器的窗口时间,多少时间内出现问题 // 静态变量static final public static final Integer WINDOWS_TIME = 20; // private static final long WINDOWS_SLEEP_TIME = 5L; // 最大失败次数,阈值 public static final Integer MAX_FAIL_COUNT = 3; /* * 当前这个断路器失败几次 * i++线程是不安全的 * AtomicInteger:原子类可以保证线程安全 * @date 2023/7/31 15:18 **/ // 当前失败的次数 private AtomicInteger currentFailCount = new AtomicInteger(0); // 使用多线程:必须要有线程池,分局部和全局,用于技术和清除失败次数 // 线程池7个参数: 核心数,最大核心数,线程等待时间,时间单位,组织队列,默认的线程工厂,线程策略 private ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor( 4, 8, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<>(2000), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy() ); // 锁对象 private Object lock = new Object(); // 如何实现每个5s内,统计到失败次数达到阈值呢? // 反向思考,没5s就清空断路器的统计次数,就可以了 { poolExecutor.execute(()->{ // 定期删除,死循环 while (true) { try { // 进来先睡几秒 TimeUnit.SECONDS.sleep(WINDOWS_TIME); } catch (InterruptedException e) { e.printStackTrace(); } // 优化:如果断路器是开的,那么不会去调用,就不会有失败,就不会记录次数,没有必要清零,这个线程可以不执行 // 清零 if (this.status.equals(FishStatus.CLOSE)){ // 清零 this.currentFailCount.set(0); }else { // 半开或者开,不需要去记录次数,这个线程可以不工作。释放掉锁 synchronized (lock) { try { lock.wait(); // 当半开调用成功以后,线程被唤醒了,往下执行,又开始了循环统计了 System.out.println("我被唤醒了,开始工作"); } catch (InterruptedException e) { e.printStackTrace(); } } } } }); } // 描述:失败后增加次数,以及修改断路器状态和重置失败次数 // 成员方案:记录失败次数 public void addFailCount() { // ++i 使用方法,获取失败次数 int i = currentFailCount.incrementAndGet(); // ++i // currentFailCount.getAndIncrement(); // i++ if (i >= MAX_FAIL_COUNT) { // 说失败次数已经到了阈值了,则断路器打开 // 修改当前状态为open this.setStatus(FishStatus.OPEN); // 当断路器打开以后,就不能去访问了,需要将他变成半开状态。 // 等待一个时间窗口,开启一个线程,让断路器变成半开 // 多线程+睡眠控制 /*new Thread(() -> { try { // 等待 TimeUnit.SECONDS.sleep(WINDOW_TIME); } catch (InterruptedException e) { e.printStackTrace(); } this.setStatus(FishStatus.HALF_OPEN); // 断路器成半开 // 重置失败次数:重置成0,不然下次进来直接就会失败 this.currentFailCount.set(0); }).start(); // 必须。start()启动线程*/ // 有局部线程池后,通过线程池来执行任务 poolExecutor.execute(()->{ try { // 等待 TimeUnit.SECONDS.sleep(WINDOWS_TIME); } catch (InterruptedException e) { e.printStackTrace(); } this.setStatus(FishStatus.HALF_OPEN); // 断路器成半开 // 重置失败次数:重置成0,不然下次进来直接就会失败 this.currentFailCount.set(0); }); } } }
<!--spring切面AOP--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
package com.tongda.aspect; import com.tongda.model.Fish; import com.tongda.model.FishStatus; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; import java.util.HashMap; import java.util.Map; import java.util.Random; import static com.tongda.model.FishStatus.CLOSE; /** * AOP切面技术:@Component配置组件+@Aspect切面注解 * 这个就类比拦截器 * 就是要判断单签断路器的状态 从而决定是否发起调用(是否执行目标方法 * @Date 2023/7/28 11:34 * @Version 1.0 */ @Component @Aspect public class FishAspect { // AOP切点 POINT_CUT=表达式,不够灵活 public static final String POINTCUT = "execution (* com.tongda.controller.FishController.doRpc(..))"; // key=哪个服务,value=该服务器提供者对应的断路器,因为一个消费者可以去调佣多个提供者,每个提供者都有自己的断路器 // 1.先在消费者里创建一个大的容器fishMap, Map<提供者,容器> public static Map<String, Fish> fishMap = new HashMap<>(); static { // 假设 是需要去调佣order-service的服务 fishMap.put("order-service",new Fish()); } // 半开时:随机数用于产生少许流量 Random random = new Random(); // 环绕通知@Around:这个就是类比拦截器,使用注解,切面 // 就是要判断,当前断路器的状态,从而决定是否发起调用(执行目标方法) @Around(value = "@annotation(com.tongda.anno.MyFish)") // @Around(value = POINTCUT) // 切点属性 public Object fishAround(ProceedingJoinPoint joinPoint){ Object result = null; // 获取到当前提供者的断路器,服务的熔断器 Fish fish = fishMap.get("order-service"); FishStatus status = fish.getStatus(); // 执行调用前先判断断路器的状态 switch (status) { // 正常: 断路器关闭状态,则远程去调用,执行目标方法 case CLOSE: try { result = joinPoint.proceed(); return result; } catch (Throwable e) { // 说明调用失败,记录次数 fish.addFailCount(); return "我是备用方案"; } // 断路器打开,不能调用,直接返回 case OPEN: return "我是备用方案"; // 断路器半开,可以用少许流量(20%)去远程调用 case HALF_OPEN: int i = random.nextInt(5); System.out.println(i); if (i == 1) { try { // 去调用 result = joinPoint.proceed(); // 说明成功了,断路器关闭 fish.setStatus(CLOSE); synchronized (fish.getLock()){ fish.getLock().notifyAll(); // 锁住,关闭后需要唤醒计数器线程开始计数。 } // 返回 return result; } catch (Throwable e) { return "我是备用方案"; } } default: return "我是备用方案"; } } }
package com.tongda.controller; import com.tongda.anno.MyFish; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.client.RestTemplate; public class FishController { @Autowired private RestTemplate restTemplate; @GetMapping("doRpc") @MyFish // public String doRpc() { // 当没有8989端口服务,要不要发起请求,配置model下FishStatus状态开关。 // 使用AOP切面技术。 String result = restTemplate.getForObject("http://localhost:8989/abc", String.class); return result; } }
熔断器切面注解
package com.tongda.anno; import java.lang.annotation.*; /** * 熔断器切面注解 * @Date 2023/7/29 10:17 * @Version 1.0 */ // 看注解使用类上还是使用方法上 @Target(ElementType.METHOD) // 注解使用在方法上 @Retention(RetentionPolicy.RUNTIME) // 作用域:运行时有效 @Documented // 文档 @Inherited // 可集成的 public @interface MyFish { }
5.Hystrix 的常用配置
hystrix: command: #用于控制HystrixCommand的行为 default: execution: isolation: strategy: THREAD #控制HystrixCommand的隔离策略,THREAD->线程池隔离策略(默认),SEMAPHORE->信号量隔离策略 thread: timeoutInMilliseconds: 1000 #配置HystrixCommand执行的超时时间,执行超过该时间会进行服务降级处理 interruptOnTimeout: true #配置HystrixCommand执行超时的时候是否要中断 interruptOnCancel: true #配置HystrixCommand执行被取消的时候是否要中断 timeout: enabled: true #配置HystrixCommand的执行是否启用超时时间 semaphore: maxConcurrentRequests: 10 #当使用信号量隔离策略时,用来控制并发量的大小,超过该并发量的请求会被拒绝 fallback: enabled: true #用于控制是否启用服务降级 circuitBreaker: #用于控制HystrixCircuitBreaker的行为 enabled: true #用于控制断路器是否跟踪健康状况以及熔断请求 requestVolumeThreshold: 20 #超过该请求数的请求会被拒绝 forceOpen: false #强制打开断路器,拒绝所有请求 forceClosed: false #强制关闭断路器,接收所有请求 requestCache: enabled: true #用于控制是否开启请求缓存 collapser: #用于控制HystrixCollapser的执行行为 default: maxRequestsInBatch: 100 #控制一次合并请求合并的最大请求数 timerDelayinMilliseconds: 10 #控制多少毫秒内的请求会被合并成一个 requestCache: enabled: true #控制合并请求是否开启缓存 threadpool: #用于控制HystrixCommand执行所在线程池的行为 default: coreSize: 10 #线程池的核心线程数 maximumSize: 10 #线程池的最大线程数,超过该线程数的请求会被拒绝 maxQueueSize: -1 #用于设置线程池的最大队列大小,-1采用SynchronousQueue,其他正数采用LinkedBlockingQueue queueSizeRejectionThreshold: 5 #用于设置线程池队列的拒绝阀值,由于LinkedBlockingQueue不能动态改版大小,使用时需要用该参数来控制线程数
实例配置
实例配置只需要将全局配置中的default换成与之对应的key即可。
hystrix: command: HystrixComandKey: #将default换成HystrixComrnandKey execution: isolation: strategy: THREAD collapser: HystrixCollapserKey: #将default换成HystrixCollapserKey maxRequestsInBatch: 100 threadpool: HystrixThreadPoolKey: #将default换成HystrixThreadPoolKey coreSize: 10
复制代码配置文件中相关key的说明
HystrixComandKey对应@HystrixCommand中的commandKey属性;
HystrixCollapserKey对应@HystrixCollapser注解中的collapserKey属性;
HystrixThreadPoolKey对应@HystrixCommand中的threadPoolKey属性。
hystrix: # hystrix的全局控制 command: default: # default是全局控制,也可以换成单个方法控制,把default换成方法名即可 fallback: circuitBreaker: #熔断器 enabled: true #开启断路器 requestVolumeThreshold: 3 # 失败次数(阈值) sleepWindowInMilliseconds: 20000 # 窗口时间 errorThresholdPercentage: 60 #失败率 execution: isolation: Strategy: thread #隔离方式 thread 线程隔离集合和 SEMAPHORE 信号量隔离级别 thread: timeoutInMilliseconds: 3000 #调用超时时长 ribbon: ReadTimeout: 5000 #要结合 feign 的底层 ribbon 调用的时长 ConnectTimeout: 5000
#隔离方式 两种隔离方式 thread 线程池 按照 group(10 个线程)划分服务提供者,用户请求的线程和做远程的线程不一样 # 好处 当 B 服务调用失败了 或者请求 B 服务的量太大了 不会对 C 服务造成影响 用户访问比较大的情况下使用比较好 异步的方式 # 缺点 线程间切换开销大,对机器性能影响 # 应用场景 调用第三方服务 并发量大的情况下 # SEMAPHORE 信号量隔离 每次请进来 有一个原子计数器 做请求次数的++ 当请求完成以后 -- # 好处 对 cpu 开销小 # 缺点 并发请求不易太多 当请求过多 就会拒绝请求 做一个保护机制 # 场景 使用内部调用 ,并发小的情况下 # 源码入门 HystrixCommand AbstractCommand HystrixThreadPool
6.Feign 的工程化实例
关系简单,无需建立其他模块。
6.1 创建父项目 feign
6.2 创建子 module
6.3 父项目 feign 的 pom.xml
<?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"> <modelVersion>4.0.0</modelVersion> <packaging>pom</packaging> <modules> <module>project-domain</module> <module>common-api</module> <module>user-center</module> <module>order-center</module> </modules> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.8</version> <relativePath/> </parent> <!--打包方式: jar还是war--> <!--管理方式:pom--> <!--<packaging>pom</packaging>--> <groupId>com.tongda</groupId> <artifactId>04-feign-project</artifactId> <version>1.0-SNAPSHOT</version> <!--全局版本号控制--> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <java.version>1.8</java.version> <spring-cloud.version>2022.0.3</spring-cloud.version> </properties> <!--全局依赖: 子模块都有--> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> <!--加载依赖管理: 这里的依赖不会真的引入项目,只是版本控制--> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <!--<version>2022.0.3</version>--> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <!--打包、仓库等配置--> <!--<build>--> <!--</build>--> </project>
common-api公共接口需要依赖domain实体类,依赖关系pom.xml中
<!--依赖domain:引入--> <dependencies> <dependency> <groupId>com.tongda</groupId> <artifactId>project-domain</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
oreder、user模块需要依赖api接口,依赖关系pom.xml中
<!--引入api依赖--> <dependencies> <dependency> <groupId>com.tongda</groupId> <artifactId>common-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
project-domain下创建order实体类
需要运行的order-center和user-center完善pom.xml
<!--web依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.7.8</version> </dependency> <!--eureka依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-netflix-eureka-client</artifactId> <version>3.1.4</version> </dependency>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.7.8</version> </plugin> </plugins> </build>
application.yml
server: port: 8080 spring: application: name: order-center eureka: client: service-url: defaultZone: http://localhost:8761/eureka instance: hostname: localhost instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
编写order-center的main方法
package com.tongda; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.core.annotation.Order; @SpringBootApplication @EnableEurekaClient public class OrderServiceApp { public static void main(String[] args) { SpringApplication.run(OrderServiceApp.class,args); } }
common-api公共接口类:引入依赖
<!--依赖domain:引入--> <dependencies> <dependency> <groupId>com.tongda</groupId> <artifactId>project-domain</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies>
UserOrderFeign接口中:
package com.tongda.feign; import lombok.extern.apachecommons.CommonsLog; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.core.annotation.Order; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; @FeignClient(value = "order-service") public interface UserOrderFeign { // 查询订单 @GetMapping("/order/getOrderByUserId") Order getOrderByUserId(@RequestParam Integer userId); }
order-center下controller中OrderController实现UserOrderFeign接口
package com.tongda.controller; import com.tongda.domain.Order; import com.tongda.feign.UserOrderFeign; import org.springframework.web.bind.annotation.RestController; @RestController // 远程调用提取,放入api公共接口,实现接口 public class OrderController implements UserOrderFeign { @Override public Order getOrderByUserId(Integer userId) { System.out.println(userId); Order order = Order.builder() .name("鱼香肉丝") .price(15D) .orderId(1) .build(); return null; } }
common-app和user-center中其中一个添加熔断器
<!--熔断器依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>2.2.10.RELEASE</version> </dependency>
user-center模块
application.yml
server: port: 8081 spring: application: name: user-center eureka: client: service-url: defaultZone: http://localhost:8761/eureka instance: hostname: localhost instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
pom.xml依赖
<?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>04-feign-project</artifactId> <groupId>com.tongda</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>user-center</artifactId> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> </properties> <!--引入api依赖--> <dependencies> <dependency> <groupId>com.tongda</groupId> <artifactId>common-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!--web依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.7.8</version> </dependency> <!--eureka依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-netflix-eureka-client</artifactId> <version>3.1.4</version> </dependency> </dependencies> </project>
编写启动类
package com.tongda; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableEurekaClient @EnableFeignClients public class UserServiceApp { public static void main(String[] args) { SpringApplication.run(UserServiceApp.class,args); } }
yml开启熔断器
server: port: 8081 spring: application: name: user-center cloud: openfeign: # openfeign下开启 circuitbreaker: enabled: true # 开启熔断器 eureka: client: service-url: defaultZone: http://localhost:8761/eureka instance: hostname: localhost instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port} hystrix: metrics: enabled: true # 开启熔断器
common-api或者user-center中编写熔断器hystrix
package com.tongda.feign.hystrix; import com.tongda.domain.Order; import com.tongda.feign.UserOrderFeign; public class UserOrderFeignHystrix implements UserOrderFeign { /* * 一般远程调用的熔断可以直接返回null * @param userId * @return {@link com.tongda.domain.Order} * * @date 2023/8/7 18:39 **/ @Override public Order getOrderByUserId(Integer userId) { return null; } }
在UserOrderFeign接口中添加指向类 fallback = 类名
启动测试