Spring Cloud Resilience4j 断路器

2023-10-24 09:59

本文主要是介绍Spring Cloud Resilience4j 断路器,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 1 概述
    • 2 基本用法
      • 2.1 断路器 CircuitBreaker
      • 2.2 限流 RateLimiter
      • 2.3 请求重试 Retry
    • 3 结合微服务
      • 3.1 准备工作
        • 3.1.1 服务注册
        • 3.1.2 服务消费
      • 3.2 断路器 CircuitBreaker
      • 3.3 限流 RateLimiter
      • 3.4 请求重试 Retry
      • 3.5 服务监控
        • 3.5.1 Prometheus
        • 3.5.1 Grafana

学习在 Spring Cloud 中使用 Resilience4j 实现断路器,包括断路器 CircuitBreaker 、限流 RateLimiter 、请求重试 Retry 等功能。

1 概述

Resilience4j 是 Spring Cloud Greenwich 版推荐的容错解决方案,相比 Hystrix , Resilience4j 专为 Java 8 以及函数式编程而设计。它主要提供了如下功能:

  1. 断路器
  2. 限流
  3. 基于信号量的隔离
  4. 缓存
  5. 限时
  6. 请求重试

2 基本用法

创建普通的 Maven 项目 resilience4j ,手动添加依赖,如下:

<dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><dependency><groupId>io.github.resilience4j</groupId><artifactId>resilience4j-circuitbreaker</artifactId><version>0.13.2</version></dependency><dependency><groupId>io.github.resilience4j</groupId><artifactId>resilience4j-ratelimiter</artifactId><version>0.13.2</version></dependency><dependency><groupId>io.github.resilience4j</groupId><artifactId>resilience4j-retry</artifactId><version>0.13.2</version></dependency>
</dependencies>

Resilience4j 提供了很多功能,不同的功能对应不同的依赖,可以按需添加。

2.1 断路器 CircuitBreaker

断路器功能相关依赖如下:

<dependency><groupId>io.github.resilience4j</groupId><artifactId>resilience4j-circuitbreaker</artifactId><version>0.13.2</version>
</dependency>
  • 测试断路器:正常情况,断路器关闭
@Test
public void testCircuitBreakerSuccess() {// 获取一个默认的 CircuitBreakerRegistry 实例// CircuitBreakerRegistry cbr = CircuitBreakerRegistry.ofDefaults();// 自定义一个 CircuitBreakerRegistry 实例CircuitBreakerConfig config = CircuitBreakerConfig.custom()// 故障率阈值百分比,超过这个阈值,断路器就会打开,这里是 50%.failureRateThreshold(50)// 断路器保持打开的时间,在到达设置的时间之后,断路器会进入到 HalfOpen 状态.waitDurationInOpenState(Duration.ofMillis(1000))// 当断路器处于 HalfOpen 状态时,环形缓冲区的大小.ringBufferSizeInHalfOpenState(2)// 当断路器处于 Closed 状态时,环形缓冲区的大小.ringBufferSizeInClosedState(2).build();CircuitBreakerRegistry cbr2 = CircuitBreakerRegistry.of(config);CircuitBreaker cb1 = cbr2.circuitBreaker("cxy35");// CircuitBreaker cb2 = cbr2.circuitBreaker("cxy352", config);CheckedFunction0<String> supplier = CircuitBreaker.decorateCheckedSupplier(cb1, () -> "hello resilience4j");Try<String> result = Try.of(supplier).map(v -> v + " hello world");System.out.println(result.isSuccess());System.out.println(result.get());
}

执行结果如下:

true
hello resilience4j hello world

  • 测试断路器:异常情况,断路器打开
@Test
public void testCircuitBreakerError() {// 自定义一个 CircuitBreakerRegistry 实例CircuitBreakerConfig config = CircuitBreakerConfig.custom()// 故障率阈值百分比,超过这个阈值,断路器就会打开,这里是 50%.failureRateThreshold(50)// 断路器保持打开的时间,在到达设置的时间之后,断路器会进入到 half open 状态.waitDurationInOpenState(Duration.ofMillis(1000))// 当断路器处于 Closed 状态时,环形缓冲区的大小.ringBufferSizeInClosedState(2).build();CircuitBreakerRegistry cbr = CircuitBreakerRegistry.of(config);CircuitBreaker cb1 = cbr.circuitBreaker("cxy35");// 模拟异常,使断路器打开System.out.println(cb1.getState());// 断路器状态cb1.onError(0, new RuntimeException());System.out.println(cb1.getState());cb1.onError(0, new RuntimeException());System.out.println(cb1.getState());CheckedFunction0<String> supplier = CircuitBreaker.decorateCheckedSupplier(cb1, () -> "hello resilience4j");Try<String> result = Try.of(supplier).map(v -> v + " hello world");System.out.println(result.isSuccess());System.out.println(result.get());
}

执行结果如下:

CLOSED
CLOSED
OPEN
falseio.github.resilience4j.circuitbreaker.CircuitBreakerOpenException: CircuitBreaker 'cxy35' is open

注意,由于 ringBufferSizeInClosedState 的值为 2 ,表示当有 2 条数据时才会去统计故障率,所以手动故障测试,至少调用 2 次 onError ,断路器才会打开。

2.2 限流 RateLimiter

限流功能相关依赖如下:

<dependency><groupId>io.github.resilience4j</groupId><artifactId>resilience4j-ratelimiter</artifactId><version>0.13.2</version>
</dependency>
  • 测试限流
@Test
public void testRateLimiter() {RateLimiterConfig config = RateLimiterConfig.custom()// 限制每 1s 处理 2 个请求.limitRefreshPeriod(Duration.ofMillis(1000)).limitForPeriod(2).timeoutDuration(Duration.ofMillis(3000)).build();RateLimiter rateLimiter = RateLimiter.of("cxy35", config);CheckedRunnable checkedRunnable = RateLimiter.decorateCheckedRunnable(rateLimiter, () -> {System.out.println(new Date());});Try.run(checkedRunnable).andThenTry(checkedRunnable).andThenTry(checkedRunnable).andThenTry(checkedRunnable).onFailure(t -> System.out.println(t.getMessage()));
}

执行结果如下:

Sun Apr 25 15:32:27 CST 2020
Sun Apr 25 15:32:27 CST 2020
Sun Apr 25 15:32:28 CST 2020
Sun Apr 25 15:32:28 CST 2020

2.3 请求重试 Retry

请求重试功能相关依赖如下:

<dependency><groupId>io.github.resilience4j</groupId><artifactId>resilience4j-retry</artifactId><version>0.13.2</version>
</dependency>
  • 测试请求重试
@Test
public void testRetry() {RetryConfig config = RetryConfig.custom()// 重试次数.maxAttempts(4)// 重试间隔.waitDuration(Duration.ofMillis(500))// 重试异常.retryExceptions(RuntimeException.class).build();Retry retry = Retry.of("cxy35", config);Retry.decorateRunnable(retry, new Runnable() {int count = 0;// 开启了重试功能之后,run 方法执行时,如果抛出异常,会自动触发重试功能@Overridepublic void run() {if (count++ < 3) {throw new RuntimeException();}}}).run();
}

当重试次数配置成 < 4 的时候,程序执行结果会抛出异常,否则不会。

3 结合微服务

3.1 准备工作

3.1.1 服务注册

创建 Spring Boot 项目 resilience4j-client-provider ,作为我们的服务提供者,添加 Web/Eureka Client 依赖,如下:

最终的依赖如下:

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency>
</dependencies>

项目创建成功后,修改 application.properties 配置文件,将 resilience4j-client-provider 注册到 Eureka Server 上(服务注册中心使用 Eureka Server ),如下:

# 当前服务的名称
spring.application.name=resilience4j-client-provider
# 当前服务的端口
server.port=5000# 服务注册中心地址
eureka.client.service-url.defaultZone=http://127.0.0.1:1111/eureka

接下来,启动 Eureka Server ,待服务注册中心启动成功后,再启动 resilience4j-client-provider ,两者都启动成功后,访问 http://127.0.0.1:1111 可以看到 resilience4j-client-provider 的注册信息。


当然 resilience4j-client-provider 也可以集群化部署,下面对 resilience4j-client-provider 进行打包,之后我们在命令行启动两个 provider 实例:

java -jar resilience4j-client-provider-0.0.1-SNAPSHOT.jar --server.port=5000
java -jar resilience4j-client-provider-0.0.1-SNAPSHOT.jar --server.port=5001

最后在 resilience4j-client-provider 提供一个 hello 接口,用于后续服务消费者 resilience4j-client-consumer 来消费,如下:

@RestController
public class ProviderController {@Value("${server.port}")Integer port; // 支持启动多个实例,做负载均衡,用端口区分@GetMapping("/hello")public String hello() {return "hello cxy35: " + port;}
}
3.1.2 服务消费

创建 Spring Boot 项目 resilience4j-client-consumer ,作为我们的服务消费者,添加 Web/Eureka Client 依赖,如下:

再手动添加 Resilience4j 相关依赖,最终的依赖如下:

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency><groupId>io.github.resilience4j</groupId><artifactId>resilience4j-spring-boot2</artifactId><version>1.2.0</version><exclusions><exclusion><groupId>io.github.resilience4j</groupId><artifactId>resilience4j-circuitbreaker</artifactId></exclusion><exclusion><groupId>io.github.resilience4j</groupId><artifactId>resilience4j-ratelimiter</artifactId></exclusion><exclusion><groupId>io.github.resilience4j</groupId><artifactId>resilience4j-bulkhead</artifactId></exclusion><exclusion><groupId>io.github.resilience4j</groupId><artifactId>resilience4j-timelimiter</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency>
</dependencies>

注意: resilience4j-spring-boot2 中包含了 Resilience4j 的所有功能,但是没有配置的功能无法使用,启动会报错,所以需要将之从依赖中剔除掉。这里先全部移除掉,下面需要测试哪块功能时,再把对应的依赖从移除列表中删除。


项目创建成功后,新建 application.yml 配置文件,将 resilience4j-client-consumer 注册到 Eureka Server 上(服务注册中心使用 Eureka Server ),如下:

# 当前服务的名称
spring:application:name: resilience4j-client-consumer
# 当前服务的端口
server:port: 5002# 服务注册中心地址
eureka:client:service-url:defaultZone: http://127.0.0.1:1111/eureka

接着,在项目启动类上添加 RestTemplate ,如下:

@SpringBootApplication
public class Resilience4jClientConsumerApplication {public static void main(String[] args) {SpringApplication.run(Resilience4jClientConsumerApplication.class, args);}@Bean@LoadBalancedRestTemplate restTemplate() {return new RestTemplate();}
}

接下来,启动 resilience4j-client-consumer ,访问 http://127.0.0.1:1111 可以看到 resilience4j-client-consumer 的注册信息。


最后在 resilience4j-client-consumer 中新增测试业务类和接口,去实现服务调用,从而消费 resilience4j-client-provider 中提供的接口,如下:

约定:本文中的服务调用失败(测试服务降级/容错),可以采用关闭某个 resilience4j-client-provider 来模拟,短时间内会报错(因为 provider 地址会缓存 consumer 上一段时间),从而达到我们的目的。

3.2 断路器 CircuitBreaker

在 resilience4j-client-provider 中修改 ProviderController ,新增接口,如下:

@GetMapping("/testCircuitBreaker")
public String testCircuitBreaker() {String s = "hello cxy35:" + port;System.out.println(s);int i = 1 / 0;return "testCircuitBreaker: " + s;
}

在 resilience4j-client-consumer 中修改 pom.xml 文件,把 resilience4j-circuitbreaker 从移除列表中删除。

然后在 application.yml 中增加断路器 CircuitBreaker 相关配置:

# Resilience4j 配置
resilience4j:# 配置断路器circuitbreaker:circuit-breaker-aspect-order: 398 # 优先级instances:cbA:ringBufferSizeInHalfOpenState: 3 # 当断路器处于 HalfOpen 状态时,环形缓冲区的大小ringBufferSizeInClosedState: 5 # 当断路器处于 Closed 状态时,环形缓冲区的大小waitInterval: 5000recordExceptions:- org.springframework.web.client.HttpServerErrorException # 记录异常

新建 ConsumerService ,新增接口,如下:

@Service
@CircuitBreaker(name = "cbA", fallbackMethod = "error") // 测试断路器,服务降级/容错
public class ConsumerService {@AutowiredRestTemplate restTemplate;public String testCircuitBreaker() {return restTemplate.getForObject("http://resilience4j-client-provider/testCircuitBreaker", String.class);}public String error(Throwable t) {return "error";}
}

其中 @CircuitBreaker 注解中的 name 属性用来指定 circuitbreaker 配置(对应配置文件), fallbackMethod 属性用来指定服务降级的方法,需要注意的是,服务降级方法中,要添加异常参数。


新建 ConsumerController ,新增接口,如下:

@RestController
public class ConsumerController {@AutowiredConsumerService consumerService;@GetMapping("/testCircuitBreaker")public String testCircuitBreaker() {return consumerService.testCircuitBreaker();}
}

访问 http://127.0.0.1:5002/testCircuitBreaker 完成测试,服务降级,返回 “error” 。

3.3 限流 RateLimiter

RateLimiter 作为限流工具,主要在服务端/服务提供者使用,用来保护服务端/服务提供者的接口。

在 resilience4j-client-provider 中修改 pom.xml 文件,手动添加 Resilience4j 相关依赖,最终的依赖如下:

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency><groupId>io.github.resilience4j</groupId><artifactId>resilience4j-spring-boot2</artifactId><version>1.2.0</version><exclusions><exclusion><groupId>io.github.resilience4j</groupId><artifactId>resilience4j-circuitbreaker</artifactId></exclusion><exclusion><groupId>io.github.resilience4j</groupId><artifactId>resilience4j-bulkhead</artifactId></exclusion><exclusion><groupId>io.github.resilience4j</groupId><artifactId>resilience4j-timelimiter</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency>
</dependencies>

注意: resilience4j-spring-boot2 中包含了 Resilience4j 的所有功能,但是没有配置的功能无法使用,启动会报错,所以需要将之从依赖中剔除掉。这里移除掉 resilience4j-ratelimiter ,用于测试限流功能。

在 resilience4j-client-provider 中修改 application.properties ,增加限流 RateLimiter 相关配置:

# 限流配置
## 定义一个限流器 rlA ,这里限制每 1s 处理 2 个请求
resilience4j.ratelimiter.limiters.rlA.limit-refresh-period=1s
resilience4j.ratelimiter.limiters.rlA.limit-for-period=2
resilience4j.ratelimiter.limiters.rlA.timeout-duration=3s

在 resilience4j-client-provider 中修改 ProviderController ,新增接口,如下:

@GetMapping("/testRateLimiter")
@RateLimiter(name = "rlA") // 测试限流
public String testRateLimiter() {String s = "hello cxy35:" + port;System.out.println(new Date());return "testRateLimiter: " + s;
}

这里通过 @RateLimiter 注解来标记该接口限流。


在 resilience4j-client-consumer 中修改 pom.xml 文件,把 resilience4j-ratelimiter 从移除列表中删除。

修改 ConsumerService ,新增接口,如下:

public String testRateLimiter() {for (int i = 0; i < 5; i++) {restTemplate.getForObject("http://resilience4j-client-provider/testRateLimiter", String.class);}return "success";
}

修改 ConsumerController ,新增接口,如下:

@GetMapping("/testRateLimiter")
public String testRateLimiter() {return consumerService.testRateLimiter();
}

访问 http://127.0.0.1:5002/testRateLimiter 完成测试, provider 中打印如下:

Mon Apr 27 15:29:51 CST 2020
Mon Apr 27 15:29:51 CST 2020
Mon Apr 27 15:29:52 CST 2020
Mon Apr 27 15:29:52 CST 2020
Mon Apr 27 15:29:53 CST 2020

3.4 请求重试 Retry

在 resilience4j-client-provider 中修改 ProviderController ,新增接口,如下:

@GetMapping("/testRetry")
public String testRetry() {String s = "hello cxy35:" + port;System.out.println(s);int i = 1 / 0;return "testRetry: " + s;
}

在 resilience4j-client-consumer 中修改 application.yml ,增加请求重试 Retry 相关配置:

# Resilience4j 配置
resilience4j:# 配置请求重试retry:retry-aspect-order: 399 # 优先级backends:retryA:maxRetryAttempts: 5 # 重试次数waitDuration: 500 # 重试等待时间exponentialBackoffMultiplier: 1.1 # 间隔乘数retryExceptions:- java.lang.RuntimeException # 重试异常

修改 ConsumerService ,新增接口,如下:

@Service
@CircuitBreaker(name = "cbA", fallbackMethod = "error") // 测试断路器,服务降级/容错
@Retry(name = "retryA") // 测试请求重试
public class ConsumerService {@AutowiredRestTemplate restTemplate;public String testRetry() {return restTemplate.getForObject("http://resilience4j-client-provider/testRetry", String.class);}
}

其中 @CircuitBreaker 注解中的 name 属性用来指定 retry 配置(对应配置文件)。


修改 ConsumerController ,新增接口,如下:

@GetMapping("/testRetry")
public String testRetry() {return consumerService.testRetry();
}

访问 http://127.0.0.1:5002/testRetry 完成测试,会重试 5 次, provider 中打印如下:

hello cxy35:5000java.lang.ArithmeticException: / by zerohello cxy35:5000java.lang.ArithmeticException: / by zero...

3.5 服务监控

微服务由于服务数量众多,所以出故障的概率很大,这种时候不能单纯的依靠人肉运维。早期的 Spring Cloud 中,服务监控主要使用 Hystrix Dashboard ,集群数据库监控使用 Turbine 。在 Greenwich 版本中,官方建议监控工具使用 Micrometer ,有如下功能:

  1. 提供了度量指标,例如 timers、counters
  2. 一揽子开箱即用的解决方案,例如缓存、类加载器、垃圾收集等等

新建一个 Spring Boot 项目,添加 Web/Actuator 依赖。项目创建成功后,添加如下配置,开启所有端点:

management.endpoints.web.exposure.include=*

然后就可以在浏览器查看项目的各项运行数据,但是这些数据都是 JSON 格式。

我们需要一个可视化工具来展示这些 JSON 数据。这里主要和大家介绍 Prometheus

3.5.1 Prometheus
# 安装
wget https://github.com/prometheus/prometheus/releases/download/v2.16.0/prometheus-2.16.0.linux-amd64.tar.gz
tar -zxvf prometheus-2.16.0.linux-amd64.tar.gz

解压完成后,配置一下数据路径和要监控的服务地址:

cd prometheus-2.16.0.linux-amd64/
vi prometheus.yml

修改 prometheus.yml 配置文件,主要改两个地方,一个是数据接口,另一个是服务地址:

接下来,将 Prometheus 整合到 Spring Boot 项目中。

首先加依赖:

<dependency><groupId>io.micrometer</groupId><artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

然后在 application.properties 配置中,添加 Prometheus 配置:

management.endpoints.web.exposure.include=*
management.endpoint.prometheus.enabled=true
management.metrics.export.prometheus.enabled=true
management.endpoint.metrics.enabled=true

接下来启动 Prometheus 。启动命令:

./prometheus --config.file=prometheus.yml

启动成功后,浏览器输入 http://192.168.91.128:9090 查看 Prometheus 数据信息。

3.5.1 Grafana

https://grafana.com/grafana/download?platform=linux


  • Spring Cloud 教程合集(微信左下方阅读全文可直达)。
  • Spring Cloud 教程合集示例代码:https://github.com/cxy35/spring-cloud-samples
  • 本文示例代码:https://github.com/cxy35/spring-cloud-samples/tree/master/spring-cloud-resilience4j

扫码关注微信公众号 程序员35 ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:https://cxy35.com

这篇关于Spring Cloud Resilience4j 断路器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/274378

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

一篇文章彻底搞懂macOS如何决定java环境

《一篇文章彻底搞懂macOS如何决定java环境》MacOS作为一个功能强大的操作系统,为开发者提供了丰富的开发工具和框架,下面:本文主要介绍macOS如何决定java环境的相关资料,文中通过代码... 目录方法一:使用 which命令方法二:使用 Java_home工具(Apple 官方推荐)那问题来了,

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三

Java中的.close()举例详解

《Java中的.close()举例详解》.close()方法只适用于通过window.open()打开的弹出窗口,对于浏览器的主窗口,如果没有得到用户允许是不能关闭的,:本文主要介绍Java中的.... 目录当你遇到以下三种情况时,一定要记得使用 .close():用法作用举例如何判断代码中的 input

Spring Gateway动态路由实现方案

《SpringGateway动态路由实现方案》本文主要介绍了SpringGateway动态路由实现方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随... 目录前沿何为路由RouteDefinitionRouteLocator工作流程动态路由实现尾巴前沿S