admin
admin
发布于 2025-12-09 / 63 阅读
4
0

SpringCloud 2025.1全面拥抱虚拟线程,你是否还要坚持你的Java8?

随着 Spring 7.xSpring Boot 4.x 的发布,Spring Cloud 也迎来了其新版本 Spring Cloud 2025.1。这一版本的发布不仅带来了常规的依赖升级和对新 JDK 版本(如 JDK 25)的支持,还迎来了对虚拟线程的全面拥抱。可以说,Spring Cloud 2025.1 是一场关于架构选择和技术取舍的革命,特别是在 Spring Cloud Gateway 组件上的“返璞归真”,为我们带来了前所未有的惊喜。

从“加法”到“减法”:Spring Cloud Gateway的拆分

Spring Cloud Gateway,一直以来作为响应式编程的代表,基于 WebFluxNetty 实现,强制要求开发者使用 Mono/Flux 等响应式编程模式来获得高性能。在过去,WebFlux + Netty 被认为是实现高并发的唯一选择,但随着虚拟线程的引入,开发者的选择空间迎来了转折点。

Spring Cloud 2025.1 中,Spring Cloud Gateway 不再是单一的响应式网关,而是被拆分成了两种技术栈,提供了 WebFluxWebMVC 两种选择。这样一来,开发者可以根据具体的需求选择适合的架构模式,甚至可以在相同的微服务架构中同时使用两种技术栈。

旧时代(已废弃):

dependencies:
  spring-cloud-starter-gateway  # 强制使用 WebFlux 和响应式编程

新时代:Spring Cloud Gateway 5.0.0 拆分后

在新的版本中,Spring Cloud Gateway 被拆分为两个独立的构件,分别适应不同的使用场景。开发者必须明确选择技术栈来启用:

  • 方案 A:继续使用 Reactive(老项目继续苟)

spring:
  threads:
    virtual:
      enabled: false  # 禁用虚拟线程,继续使用响应式编程
dependencies:
  spring-cloud-starter-gateway-server-webflux  # WebFlux + Netty(保持响应式架构)
  • 方案 B:拥抱虚拟线程(新项目必须上)

spring:
  threads:
    virtual:
      enabled: true  # 启用虚拟线程
dependencies:
  spring-cloud-starter-gateway-server-webmvc  # WebMVC + 虚拟线程(提高性能)

为什么要拆分?虚拟线程时代的到来

要理解 Spring Cloud Gateway 的拆分决策,必须理解 Java 21 引入的虚拟线程(Virtual Threads)如何彻底改变了传统架构设计。过去,开发者在面对高并发时,面临的主要选择是两种模式:

  1. 阻塞模型(传统模式):每个请求一个线程,线程资源昂贵且容易耗尽。

  2. 非阻塞模型(响应式编程):通过 CallbackMono/Flux 等方式实现非阻塞操作,虽然能节省线程,但编程复杂度较高。

虚拟线程的出现改变了这一局面。Java 21 中,虚拟线程与操作系统的内核线程解耦,阻塞操作不再消耗宝贵的内核线程资源。虚拟线程能够在处理阻塞操作时,极大地减少线程上下文切换的开销,提供类似于 非阻塞 的高并发性能。

WebFlux VS WebMVC:虚拟线程的优势

WebFlux + Netty 模式(基于响应式编程)在处理高并发时,性能非常优秀,但却有较高的编程复杂度,且调试与监控较为困难。相比之下,传统的 WebMVC + 虚拟线程 模式在性能上几乎不逊色,但代码复杂度大大降低,开发者可以回归到传统的同步编程模型。

为了展示两种技术栈的区别,我们来实现一个 GoodsController。这个控制器将分别使用 WebMVC + 虚拟线程WebFlux 两种方式来实现。

示例 1:WebMVC + 虚拟线程(传统阻塞模型)

@RestController
@RequestMapping("/goods")
public class GoodsController {

    private final GoodsService goodsService;

    public GoodsController(GoodsService goodsService) {
        this.goodsService = goodsService;
    }

    @GetMapping("/{id}")
    public Goods getGoods(@PathVariable Long id) {
        // 这是一个阻塞调用,但虚拟线程允许我们在高并发下不消耗内核线程
        Goods goods = goodsService.findById(id);  // 阻塞调用
        return goods;
    }

    @GetMapping
    public List<Goods> getAllGoods() {
        // 批量获取商品,仍然是阻塞调用,虚拟线程将确保高并发下线程不会被耗尽
        return goodsService.findAll();
    }
}

示例 2:WebFlux + Netty(响应式编程)

@RestController
@RequestMapping("/goods")
public class GoodsController {

    private final GoodsService goodsService;

    public GoodsController(GoodsService goodsService) {
        this.goodsService = goodsService;
    }

    @GetMapping("/{id}")
    public Mono<Goods> getGoods(@PathVariable Long id) {
        // 使用响应式编程模式,返回 Mono 对象,表示异步操作
        return goodsService.findById(id);
    }

    @GetMapping
    public Flux<Goods> getAllGoods() {
        // 使用响应式编程返回 Flux 对象,表示多个商品的异步操作
        return goodsService.findAll();
    }
}

性能对比:WebFlux + Netty vs WebMVC + 虚拟线程

基于官方基准测试,以下是两者的性能对比:

  • WebFlux + Netty:30000 RPS,95% 延迟 45ms

  • WebMVC + 虚拟线程:28500 RPS,95% 延迟 48ms

可以看到,WebMVC + 虚拟线程 的性能与 WebFlux + Netty 差距不足 5%,而且在开发效率和代码简洁性上,后者的优势更加明显。

虚拟线程对其他 Spring Cloud 组件的影响

虚拟线程的普及不仅影响了 Spring Cloud Gateway,还直接影响了 Spring Cloud Stream 的架构。例如,曾经作为高性能方案的 Reactive Kafka Binder 被移除。过去,Reactive Kafka 因为支持非阻塞背压机制,曾被认为是“终极方案”,但其实现复杂,错误处理和调试困难。现在,虚拟线程使得阻塞模式重新焕发活力,开发者可以在高并发的环境中轻松使用传统的阻塞模式,而无需依赖复杂的响应式编程。

启用虚拟线程后的最佳实践

当使用启用了虚拟线程的 WebMVC 版本的 Spring Cloud Gateway 时,可以通过以下配置来支持百万级并发:

spring:
  threads:
    virtual:
      enabled: true  # 启用
      executor:
        max-thread-count: 1000000  # 线程数量配置

tomcat:
  threads:
    max: 1000  # Servlet 线程池仅负责调度,设置较小值即可

总结:虚拟线程时代的回归

Spring Cloud 2025.1 的发布标志着一个重要的转折点:响应式编程不再是获取高性能的唯一方式,传统的 WebMVC 配合 虚拟线程 也能达到接近的性能,并且简化了开发过程。这不仅是性能上的技术选择,更是开发哲学的回归:简单胜过复杂,实用胜过炫技,生态兼容胜过极致性能。

随着虚拟线程的推广,Spring Cloud 的微服务架构将迎来新的发展机遇,而开发者也将能够享受到更加高效、简洁的开发体验。


评论