企业级开发领域正在经历一场翻天覆地的巨变,然而大多数开发者却对此浑然不觉,完全没有意识到。Spring Boot 3.5 带来的革命性的虚拟线程 (Virtual Threads) 和增强的响应式能力,绝不仅仅是小打小闹的增量改进——它们正在从根本上改变我们对异步处理的思考方式,甚至可能让传统的消息队列在许多应用场景中变得过时

在我将三个生产系统从重度依赖 RabbitMQ 的架构迁移到 Spring Boot 3.5 的原生异步模式后,我亲眼见证了其性能提升之巨大,足以挑战我们过去对可扩展系统设计的所有认知


消息队列的“垄断地位”正在崩塌

多年以来,消息队列一直是服务解耦和处理异步任务的首选方案。Redis、RabbitMQ、Apache Kafka——这些工具在我们的架构模式中变得如此根深蒂固,以至于质疑它们的必要性都感觉像是“异端邪说”。

但是,Spring Boot 3.5 彻底改变了游戏规则。随着 Loom 项目的虚拟线程进入生产就绪阶段并与框架无缝集成,Spring Boot 现在能够处理数百万级别的并发操作,而无需承受以往迫使我们转向消息队列模式的那种高昂开销。

看个例子:

import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
// 假设 Order, OrderResult, validateOrder, calculatePricing, reserveInventory, processPayment 已定义@Service
public class OrderProcessingService {// 使用名为 "virtualThreadExecutor" 的执行器来运行异步方法@Async("virtualThreadExecutor")public CompletableFuture<OrderResult> processOrder(Order order) {// 这个方法现在会在一个虚拟线程上运行// 使用 CompletableFuture.supplyAsync 可以在指定的执行器上异步执行任务return CompletableFuture.supplyAsync(() -> {// 模拟订单处理步骤System.out.println("在虚拟线程 " + Thread.currentThread() + " 中处理订单: " + order.getId());validateOrder(order);calculatePricing(order);reserveInventory(order);processPayment(order);System.out.println("订单 " + order.getId() + " 处理完毕。");return new OrderResult(order.getId(), "成功"); // 假设 OrderResult 构造器}, virtualThreadExecutor()); // 明确指定使用虚拟线程执行器}// 定义一个使用虚拟线程的 Executor Bean@Beanpublic Executor virtualThreadExecutor() {// Executors.newVirtualThreadPerTaskExecutor() 会为每个任务创建一个新的虚拟线程return Executors.newVirtualThreadPerTaskExecutor();}// 模拟的辅助方法private void validateOrder(Order order) { try { Thread.sleep(50); } catch (InterruptedException e) {} }private void calculatePricing(Order order) { try { Thread.sleep(50); } catch (InterruptedException e) {} }private void reserveInventory(Order order) { try { Thread.sleep(50); } catch (InterruptedException e) {} }private void processPayment(Order order) { try { Thread.sleep(50); } catch (InterruptedException e) {} }// 假设的 Order 和 OrderResult 类// static record Order(String id) {}// static record OrderResult(String orderId, String status) {}
}

性能革命:真实数据说话

在我最近一次对比测试中,我将一个传统的 Spring Boot 2.7 应用(使用 RabbitMQ 进行异步处理)与一个 Spring Boot 3.5 应用(使用虚拟线程进行原生异步处理)进行了比较,结果令人瞠目欲舌

  • • 传统方案 (Spring Boot 2.7 + RabbitMQ):

    • • 峰值吞吐量:5,000 请求/秒

    • • 高负载下内存使用:2.1GB

    • • 平均延迟:250毫秒

    • • 基础设施复杂度:6个组件(应用服务、API网关、RabbitMQ集群、消费者服务、数据库、可能的负载均衡器)

  • • Spring Boot 3.5 原生异步方案:

    • • 峰值吞吐量:18,000 请求/秒

    • • 高负载下内存使用:850MB

    • • 平均延迟:65毫秒

    • • 基础设施复杂度:2个组件(应用服务(包含虚拟线程处理)、数据库)

通过消除消息序列化、网络跳数以及队列管理的开销,原生异步方案带来了 260% 的性能提升,同时将基础设施复杂度降低了 67%


架构范式的转变

以下是架构范式如何转变的示意:

  • • 传统消息队列架构:
    ┌─────────────┐    ┌──────────────┐    ┌─────────────┐    ┌──────────────┐
    │   客户端    │───▶│  API 网关    │───▶│  消息队列   │───▶│   工作者服务 │
    └─────────────┘    └──────────────┘    └─────────────┘    └──────────────┘│              (多个独立的消费者)▼┌─────────────┐│   数据库    │└─────────────┘
  • • Spring Boot 3.5 原生异步架构:
    ┌─────────────┐    ┌──────────────────────────────┐    ┌─────────────┐
    │   客户端    │───▶│  Spring Boot 应用 (内含异步处理) │───▶│   数据库    │
    └─────────────┘    └──────────────────────────────┘    └─────────────┘│  虚拟线程池 (可支持数百万并发)   │└───────────────────────────────┘
    (应用内部通过虚拟线程处理了原本需要消息队列和独立工作者服务才能完成的异步任务)

真实世界实现:电子商务订单处理

让我带你体验一个真实的实现案例,它彻底取代了我们之前基于 RabbitMQ 的整个订单处理系统:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors; // 用于创建虚拟线程执行器
// 假设 OrderRequest, OrderResponse, OrderResult, Order 等 DTO 和实体已定义@RestController
public class OrderController {@Autowiredprivate OrderOrchestrator orchestrator; // 注入订单编排服务@PostMapping("/orders")public ResponseEntity<OrderResponse> createOrder(@RequestBody OrderRequest request) {// 这里调用编排器,它会处理整个异步处理流水线,无需消息队列CompletableFuture<OrderResult> futureResult = orchestrator.processOrderPipeline(request);try {// 在Controller中,通常我们会立即返回,或者提供一个查询任务状态的接口。// 此处的 future.join() 会阻塞等待结果,仅为演示。// 生产环境中,可以返回一个任务ID,让客户端轮询,或使用回调/WebSocket通知。OrderResult result = futureResult.join(); // 等待异步流程完成并获取结果return ResponseEntity.ok(new OrderResponse(result.orderId(), "处理成功", result.details()));} catch (Exception e) { // CompletableFuture.join() 会在异常时抛出 CompletionExceptionreturn ResponseEntity.status(500).body(new OrderResponse(null, "处理失败", e.getMessage()));}}
}@Component
class OrderOrchestrator {// 假设已通过 @Bean 定义并注入名为 virtualThreadExecutor 的虚拟线程执行器// @Autowired @Qualifier("virtualThreadExecutor") private Executor virtualThreadExecutor;// 或者直接在这里创建,但不推荐在组件内部创建 Executorprivate final Executor virtualThreadExecutor = Executors.newVirtualThreadPerTaskExecutor();public CompletableFuture<OrderResult> processOrderPipeline(OrderRequest request) {// 使用 CompletableFuture 构建异步处理流水线return CompletableFuture.supplyAsync(() -> validateOrder(request), virtualThreadExecutor) // 1. 异步校验订单.thenComposeAsync(validatedOrder -> reserveInventory(validatedOrder), virtualThreadExecutor) // 2. 异步预留库存.thenComposeAsync(inventoryReservedOrder -> processPayment(inventoryReservedOrder), virtualThreadExecutor) // 3. 异步处理支付.thenComposeAsync(paymentProcessedOrder -> updateInventory(paymentProcessedOrder), virtualThreadExecutor) // 4. 异步更新库存(实际扣减).thenComposeAsync(inventoryUpdatedOrder -> sendNotifications(inventoryUpdatedOrder), virtualThreadExecutor); // 5. 异步发送通知}// --- 以下为模拟的业务方法,每个都在虚拟线程上执行 ---private Order validateOrder(OrderRequest request) {System.out.println("校验订单 (线程: " + Thread.currentThread() + ")");// ... 校验逻辑 ...return new Order(request.orderId(), "VALIDATED"); // 返回处理后的 Order 对象或中间结果}private Order reserveInventory(Order order) {System.out.println("预留库存 (线程: " + Thread.currentThread() + ")");// ... 预留库存逻辑 ...return new Order(order.orderId(), "INVENTORY_RESERVED");}private Order processPayment(Order order) {System.out.println("处理支付 (线程: " + Thread.currentThread() + ")");// ... 支付逻辑 ...return new Order(order.orderId(), "PAYMENT_PROCESSED");}private Order updateInventory(Order order) {System.out.println("更新库存 (线程: " + Thread.currentThread() + ")");// ... 更新库存逻辑 ...return new Order(order.orderId(), "INVENTORY_UPDATED");}private OrderResult sendNotifications(Order order) {System.out.println("发送通知 (线程: " + Thread.currentThread() + ")");// ... 发送通知逻辑 ...return new OrderResult(order.orderId(), "COMPLETED", "所有步骤完成");}// 假设的 DTO/实体类// static record OrderRequest(String orderId) {}// static record Order(String orderId, String status) {}// static record OrderResponse(String orderId, String message, String details) {}// static record OrderResult(String orderId, String finalStatus, String details) {}
}

一个服务就取代了我们以前需要通过 RabbitMQ 队列连接的四个独立的微服务,极大地降低了部署复杂性,并消除了多个潜在的故障点。


反方观点:消息队列何时仍然重要?

批评者会理直气壮地指出,在某些场景下,消息队列仍然是不可替代的。例如,事件溯源 (Event Sourcing) 架构、跨多个异构系统的集成,以及那些需要严格保证消息投递和持久化的场景,仍然能从专用的消息代理(如 Kafka, RabbitMQ)中受益。

然而,这些必须使用消息队列的场景范围正在迅速缩小。Spring Boot 3.5 增强的事务管理能力(如 @Transactional 的良好支持)和故障恢复机制(如 @Retryable)已经能够处理绝大多数以前需要消息队列来保证的可靠性问题。

import org.springframework.retry.annotation.Retryable;
import org.springframework.transaction.annotation.Transactional;
import java.util.concurrent.CompletableFuture;
// 假设 Task, ProcessingResult, executeBusinessLogic, virtualThreadExecutor 已定义// @Service
// public class ReliableProcessor {
//     @Transactional(rollbackFor = Exception.class) // 确保操作的原子性
//     @Retryable(value = {Exception.class}, maxAttempts = 3, backoff = @Backoff(delay = 1000)) // 失败时自动重试
//     public CompletableFuture<ProcessingResult> processWithReliability(Task task) {
//         return CompletableFuture.supplyAsync(() -> {
//             // 这里的业务逻辑将在一个事务内执行,并且在失败时会自动重试
//             return executeBusinessLogic(task);
//         }, virtualThreadExecutor()); // 在虚拟线程上执行
//     }
// }

开发者体验的革命

与性能提升相比,认知负荷的降低或许更为重要。开发者不再需要:

  • • 设计消息的 schema(结构)和序列化策略。

  • • 管理队列的配置和死信队列 (DLQ)。

  • • 处理复杂的消息路由和交换机模式 (exchange patterns)。

  • • 跨队列边界进行头疼的分布式追踪和调试。

  • • 维护和部署独立的消费者应用程序。

取而代之的是,整个异步处理流水线都存在于开发者熟悉的 Spring Boot 模式之中,使得调试、测试和维护都变得异常简单


生产环境迁移策略

对于考虑进行这种转变的团队,以下是我们行之有效的分阶段迁移方法:

阶段 1:新功能试点 (低风险)
┌─────────────────────┐
│  对新的异步功能,   │
│  直接使用虚拟线程   │
│  进行实现。         │
└─────────────────────┘▼
阶段 2:非核心服务迁移 (中等风险)
┌─────────────────────┐
│  迁移如报表、分析等  │
│  对实时性要求稍低的  │
│  非核心服务。       │
└─────────────────────┘▼
阶段 3:核心服务改造 (高风险)
┌─────────────────────┐
│  在核心业务逻辑中,  │
│  逐步替换掉原有的    │
│  消息队列模式。     │
└─────────────────────┘

行业影响与未来预测

各大云服务提供商已经在积极适应这一变化。AWS 最近宣布在其容器服务中增强了对虚拟线程工作负载的支持,而 Google Cloud 也正在专门为 Project Loom 模式优化其 JVM 实现。

我预测,到 2026 年,用于内部服务间通信的消息队列使用量将减少 40%,队列将主要被“降级”用于处理外部系统集成大规模事件流等特定场景。


底线:简单制胜 (Simplicity Wins)

消息队列的时代确实解决了许多棘手的问题,但它同时也引入了额外的复杂性,而 Spring Boot 3.5 的出现使得这些复杂性在很多场景下已不再必要。虚拟线程提供了异步处理所带来的可伸缩性优势,却没有传统分布式消息系统那样沉重的运维开销。

对于全新的项目 (greenfield projects),选择是明确的:直接从 Spring Boot 3.5 的原生异步能力开始,只有当特定的、复杂的需求(如需要持久化队列、跨语言通信等)真正出现时,才考虑引入消息队列。

对于现有的系统,可以开始尝试在新功能中使用虚拟线程模式,同时为核心服务规划战略性的迁移。

企业级开发的未来趋势是更简单、更快、更易于维护——而且,它不再需要为每一个异步操作都配备一个消息队列了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/82351.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/82351.shtml
英文地址,请注明出处:http://en.pswp.cn/web/82351.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络编程(计算机网络基础)

认识网络 1.网络发展史 ARPnetA(阿帕网)->internet(因特网)->移动互联网->物联网 2.局域网与广域网 局域网 概念&#xff1a;的缩写是LAN&#xff08;local area network&#xff09;&#xff0c;顾名思义&#xff0c;是个本地的网络&#xff0c;只能实现小范围短距…

Windows Server部署Vue3+Spring Boot项目

在Windows Server 上部署Vue3 Spring Boot前后端分离项目的详细步骤如下&#xff1a; 一、环境准备 安装JDK 17 下载JDK MSI安装包&#xff08;如Oracle JDK 或 OpenJDK&#xff09; 双击安装&#xff0c;配置环境变量&#xff1a; JAVA_HOME&#xff1a;JDK安装路径&#xf…

CCF CSP 第37次(2025.03)(3_模板展开_C++)(哈希表+stringstream)

CCF CSP 第37次&#xff08;2025.03&#xff09;&#xff08;3_模板展开_C&#xff09; 解题思路&#xff1a;思路一&#xff08;哈希表stringstream&#xff09;&#xff1a; 代码实现代码实现&#xff08;思路一&#xff08;哈希表stringstream&#xff09;&#xff09;&…

数据安全管理进阶:81页 2024数据安全典型场景案例集【附全文阅读】

《2024 数据安全典型场景案例集》聚焦政务数据安全&#xff0c;覆盖数据细粒度治理、授权运营、接口安全、系统接入、批量数据共享、使用侧监管、风险监测、账号管控、第三方人员管理、密码应用等十大典型场景&#xff0c;剖析各场景风险并提供技术方案&#xff0c;如基于 AI 的…

Leetcode 261. 以图判树

1.题目基本信息 1.1.题目描述 给定编号从 0 到 n - 1 的 n 个结点。给定一个整数 n 和一个 edges 列表&#xff0c;其中 edges[i] [ai, bi] 表示图中节点 ai 和 bi 之间存在一条无向边。 如果这些边能够形成一个合法有效的树结构&#xff0c;则返回 true &#xff0c;否则返…

【ISAQB大纲解读】LG 1-8:区分显性陈述和隐性假设(R1)

软件架构师&#xff1a; 应明确提出假设或先决条件&#xff0c;从而防止隐性假设 知道隐性假设可能会导致利益相关方之间的潜在误解 1. 应明确提出假设或先决条件&#xff0c;防止隐性假设 为什么重要&#xff1f; 隐性假设是架构风险的温床 例如&#xff1a;假设“所有服务都…

IT运维工具的选择标准有哪些?

选择IT运维工具时&#xff0c;可参考以下标准&#xff0c;确保工具适配业务需求且高效易用&#xff1a; 1. 明确业务需求与场景 • 核心目标&#xff1a;根据运维场景&#xff08;如监控、自动化、安全等&#xff09;匹配工具功能。例如&#xff0c;监控大规模集群选Promethe…

MySQL 核心知识整理【一】

一、MySQL存储引擎对比&#xff1a;InnoDB vs MyISAM 在使用MySQL时&#xff0c;选择合适的存储引擎对性能影响很大。最常见的两个引擎是 InnoDB 和 MyISAM&#xff0c;它们各自的设计目标不同&#xff0c;适用场景也不一样。 事务与数据安全性方面&#xff0c;InnoDB 支持事…

人工智能在智能制造业中的创新应用与未来趋势

随着工业4.0和智能制造的快速发展&#xff0c;人工智能&#xff08;AI&#xff09;技术正在深刻改变制造业的各个环节。从生产自动化到质量检测&#xff0c;从供应链优化到设备维护&#xff0c;AI的应用不仅提高了生产效率&#xff0c;还提升了产品质量和企业竞争力。本文将探讨…

arc3.2语言sort的时候报错:(sort < `(2 9 3 7 5 1)) 需要写成这种:(sort > (pair (list 3 2)))

arc语言sort的时候报错&#xff1a;(sort < (2 9 3 7 5 1)) arc> (sort < (2 9 3 7 5 1)) Error: "set-car!: expected argument of type <pair>; given: 9609216" arc> (sort < (2 9 3 )) Error: "Function call on inappropriate object…

Ubuntu 24.04 LTS Chrome 中文输入法(搜狗等)失效?一行命令解决

Ubuntu 24.04 LTS Chrome 中文输入法&#xff08;搜狗等&#xff09;失效&#xff1f;一行命令解决 在 Ubuntu 24.04 LTS 中&#xff0c;如果你发现 Chrome 浏览器用不了搜狗输入法或其他 Fcitx5 中文输入法&#xff0c;可以试试下面的方法。 直接上解决方案&#xff1a; 打…

大模型前处理-CPU

前处理包含哪些流程 分词 tokenizationembedding CPU可以做哪些优化 分词 分词在做什么&#xff1f; 什么是词元化&#xff1f; 词元化&#xff08;Tokenization&#xff09;是把一段自然语言文本拆分成更小的单元&#xff08;称为“词元”&#xff0c;即 Token&#xff0…

Kafka数据怎么保障不丢失

在分布式消息系统中&#xff0c;数据不丢失是核心可靠性需求之一。Apache Kafka 通过生产者配置、副本机制、持久化策略、消费者偏移量管理等多层机制保障数据可靠性。以下从不同维度解析 Kafka 数据不丢失的核心策略&#xff0c;并附示意图辅助理解。 一、生产者端&#xff1a…

图像处理篇---face_recognition库实现人脸检测

以下是使用face_recognition库实现人脸检测的详细步骤、实例代码及解释&#xff1a; 一、环境准备 1. 安装依赖库 pip install face_recognition opencv-python # 核心库 pip install matplotlib # 用于显示图像&#xff08;可选&#xff09;2. 依赖说明 face_recognitio…

vb.net oledb-Access 数据库本身不支持命名参数,赋值必须和参数顺序一致才行

参数顺序问题&#xff1a;OleDb 通常依赖参数添加的顺序而非名称,为什么顺序要一样? OleDbParameter 顺序依赖性的原因 OleDb 数据提供程序依赖参数添加顺序而非名称&#xff0c;这是由 OLE DB 规范和 Access 数据库的工作机制共同决定的。理解这个问题需要从数据库底层通信…

Syslog 全面介绍及在 C 语言中的应用

Syslog 概述 Syslog 是一种工业标准的日志记录协议&#xff0c;用于在网络设备之间传递日志消息。它最早由 Eric Allman 在 1980 年代为 BSD Unix 开发&#xff0c;现在已成为系统和网络管理的重要组成部分。Syslog 协议允许设备将事件消息发送到中央服务器&#xff08;称为 sy…

HackMyVM-Art

信息搜集 主机发现 ┌──(kali㉿kali)-[~] └─$ nmap -sn 192.168.43.0/24 Starting Nmap 7.95 ( https://nmap.org ) at 2025-05-31 03:00 EDT Nmap scan report for 192.168.43.1 Host is up (0.0047s latency). MAC Address: C6:45:66:05:91:88 (Unknown) Nmap scan rep…

[paddle]paddle2onnx无法转换Paddle3.0.0的json格式paddle inference模型

使用PDX 3.0rc1 训练时序缺陷检测后导出的模型无法转换 Informations (please complete the following information): Inference engine for deployment: PD INFERENCE 3.0-->onnxruntime Why convert to onnx&#xff1a;在端侧设备上部署 Paddle2ONNX Version: 1.3.1 解…

DOCKER使用记录

1、拉取镜像 直接使用docker pull <image>&#xff0c;大概率会出现下面的报错信息&#xff1a; (base) jetsonyahboom:~$ docker pull ubuntu:18.04 Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while …

Java实习面试题

一、理想汽车一面 1、总结你这个人擅长什么&#xff0c;你的优势是什么&#xff1f; 2、挑一个项目详细讲讲&#xff0c;重点讲下你怎么设计的&#xff0c;你的思路是什么&#xff0c;你做的过程中遇到什么难点&#xff0c;怎么克服这些难点&#xff1f; 3、使用RabbitMQ处理…