Vert.x — Eclipse 出品的 polyglot reactive JVM toolkit,用事件总线 + verticle 把 Node.js 那套搬到多语言
是什么
Vert.x 是 Tim Fox(前 VMware / RedHat 工程师)2011 年起在 VMware 启动、2013 年捐给 Eclipse Foundation 的 JVM 上的 reactive toolkit。日常类比:像一个开放式厨房——传统 Java 后端是『一桌一服务员』(每个请求一个线程,等菜时人也站那不动),Vert.x 是『一个服务员管整个区域』(事件循环线程不停转,谁的菜好了就去端,自己绝不站着等)。
它不是 Spring 那种 application framework,更像一个 toolkit:
- vertx-core:提供 event loop / verticle / event bus / future 这套基础设施
- vertx-web:在 core 上加 router / body handler / cookie / auth 等 HTTP 套件
- 60+ 周边模块:vertx-jdbc-client / vertx-mqtt / vertx-grpc / vertx-cassandra-client……都是非阻塞客户端
public class HelloVerticle extends AbstractVerticle { public void start() { vertx.createHttpServer() .requestHandler(req -> req.response().end("hello")) .listen(8080); }}短短几行就是一个 event loop 驱动的 HTTP 服务,单机吞吐能压过 Spring Boot 一个数量级。
为什么重要
不理解 Vert.x,下面这些事都没法解释:
- 为什么 quarkus 这种『Java 启动比 Node 还快』的云原生框架能跑得动 reactive 路线——Quarkus 底层 reactive 引擎就是 Vert.x
- 为什么 JVM 世界除了 spring-boot 之外还有第二条主流路线——非阻塞 / event-loop 路线由 Vert.x 长期扛旗
- 为什么 Eclipse Foundation 在 Java EE 之外还押宝一个独立 toolkit——polyglot + reactive 是 Java 反击 Node.js 的关键武器
- 为什么很多公司的网关 / 实时推送场景会选 Vert.x——单机 10w+ 长连接,传统阻塞模型扛不住
核心要点
Vert.x 可以拆成 三个支柱:
-
Event loop + 非阻塞 Golden Rule:每个 CPU 核默认起 2 个 event loop 线程,verticle 永远在自己绑定的那个线程上跑。代码里不准调用任何阻塞 API(同步 JDBC、Thread.sleep、阻塞 read),否则整条 loop 上挂着的几千个连接全卡。类比:高速公路上一辆车熄火,整条路都堵。
-
Verticle —— 部署单元:verticle 是 Vert.x 的『最小活的东西』,类比 Erlang actor 但更轻。每个 verticle 单线程跑,所以内部写代码不需要加锁;要和别的 verticle 通信,走 event bus 发消息。standard verticle 跑在 event loop 上、worker verticle 跑在 worker pool 上做阻塞活。
-
Event Bus —— 进程内 + 跨进程统一总线:所有 verticle 间通信走
eventBus.send(address, msg)或publish。本机就是内存调用,集群模式下底层走 Hazelcast / Infinispan,跨节点透明。类比:邮局——你不管收件人在隔壁工位还是另一座城市,都丢邮局,地址相同就送到。
三个支柱合起来:单线程 event loop 保证无锁、verticle 切分关注点、event bus 把进程内外通信统一。
实践案例
案例 1:HTTP 服务 + 路由
public class WebVerticle extends AbstractVerticle { public void start() { Router router = Router.router(vertx); router.get("/hello/:name").handler(ctx -> { String name = ctx.pathParam("name"); ctx.response().end("hi, " + name); }); vertx.createHttpServer().requestHandler(router).listen(8080); }}逐部分解释:
AbstractVerticle.start()是 verticle 启动钩子,类似 mainRouter来自 vertx-web 模块,类比 express 的app,但 handler 永远不能阻塞:name路径参数的取法和 Express 几乎一样,照搬体感降低门槛- 整个 server 跑在单 event loop 线程,几千并发连接共用这条线程,全靠非阻塞 IO
案例 2:Event Bus 跨 verticle 通信
// VerticleA 发消息vertx.eventBus().<JsonObject>request("user.lookup", new JsonObject().put("id", 1), reply -> { if (reply.succeeded()) System.out.println(reply.result().body());});
// VerticleB 收消息vertx.eventBus().<JsonObject>consumer("user.lookup", msg -> { JsonObject body = msg.body(); msg.reply(new JsonObject().put("name", "alice"));});逐部分解释:
request是『发了等回信』模式(点对点 + reply),send是单向,publish是广播- 消息体走 codec 序列化,JsonObject / String / Buffer 都内置
- 这套 API 单机和集群一模一样——本地就是内存传递,集群就走 Hazelcast 同步
- 解耦极强:A 不知道 B 在哪个 verticle / 哪台机器,只认 address 字符串
案例 3:Future 链式组合替代回调地狱
client.preparedQuery("SELECT id FROM users WHERE name=$1").execute(Tuple.of("alice")) .compose(rs -> { Long id = rs.iterator().next().getLong("id"); return client.preparedQuery("SELECT * FROM orders WHERE user_id=$1").execute(Tuple.of(id)); }) .onSuccess(orders -> ctx.response().end(orders.toString())) .onFailure(err -> ctx.fail(500, err));逐部分解释:
- Vert.x 4 把 v3 的
Handler<AsyncResult<T>>回调换成强类型Future<T> compose类比 JS Promise 的then,第一步结果传第二步- 失败统一在
onFailure,避免每层 if (succeeded) 检查 - 想更顺手可以接 RxJava 3 / Kotlin coroutine / Mutiny(Quarkus 风格),都有官方 binding
踩过的坑
-
阻塞 event loop 是 1 号杀手:写习惯了 Spring 的人随手
jdbcTemplate.query或Thread.sleep,整个 event loop 上几千连接立刻卡。Vert.x 启动后会 warnThread blocked for X ms,看到就停下来。解法:用非阻塞客户端(vertx-pg-client / reactive-mysql-client),不得已的同步代码塞vertx.executeBlocking丢去 worker pool。 -
Future API 异常 stack trace 断成两截:异步链路里抛异常,trace 只看到 event loop 调度的那一帧,看不到业务调用方。解法:开
-Dvertx.disableContextTimings=false和ContextInternal的 trace;或者迁到 RxJava / Kotlin coroutine,框架会重建调用链。 -
verticle 当 actor 用,状态共享出 bug:新人见到 verticle 第一反应是『每个用户一个 verticle 存会话』,忘了 verticle 之间应该走 event bus。直接持有别的 verticle 引用调方法,立刻跨线程并发。解法:守住一条铁律——verticle 之间只用 event bus 通信,需要共享数据用
SharedData(lock-free map)。 -
集群模式 split-brain 难调:默认 Hazelcast 集群在网络抖动下可能脑裂,event bus 消息看似送出去其实没人收。解法:生产用 Infinispan 替换 Hazelcast 集群管理器,或上 kubernetes 用 vertx-k8s-discovery;监控
vertx.eventbus.handlers.count指标。
适用 vs 不适用场景
适用:
- 高并发长连接场景(WebSocket / SSE / MQTT)—— 单机 10w+ 连接是日常
- 网关 / 反向代理 / 协议转换层 —— 几乎纯 IO,event loop 优势最大
- 需要 polyglot 团队协作 —— Java / Kotlin / JS 共享同一套调度
- 已经在 quarkus / Microprofile 体系内 —— Vert.x 是底层引擎
不适用:
- 重事务 / 重 ORM 的传统 CRUD 业务 —— Hibernate / JPA 都是阻塞,硬拗反而退步,spring-boot 更顺手
- 团队没 reactive 经验且工期紧 —— Future / 非阻塞思维学习曲线陡,先用 spring-boot 上线再考虑迁
- CPU 密集型计算(图像 / ML 推理)—— event loop 优势在 IO,CPU 密集还是要 worker pool 或专用框架
- 单机小流量内部工具 —— 收益小,复杂度高
历史小故事(可跳过)
- 2011 年:Tim Fox 在 VMware 启动
Node.x项目,目标是『把 Node.js event loop 搬到 JVM,但允许多语言』 - 2012-05:项目改名 Vert.x v1.0 发布,避免和 Node.js 名字冲突
- 2013 年:Tim Fox 跳槽 RedHat,把项目捐给 Eclipse Foundation 独立运作
- 2015 年:v3.0 重构,polyglot 体系靠 codegen 生成各语言绑定,不再手写多份
- 2020-03:v4.0 弃用
Handler<AsyncResult>改为强类型Future<T>,向现代 reactive API 靠拢 - 2024 年:v5 milestone 阶段引入 Project Loom virtual threads 兼容,让 verticle 可声明跑在虚拟线程上,写起来像同步代码
学到什么
- event loop 模型不是 Node.js 专利:JVM 上靠 Netty + Vert.x 同样能做到单线程几千连接,关键是『绝不阻塞』这条铁律
- Toolkit 比 Framework 更轻:Vert.x 不强求项目结构、不接管启动流程,你按需用模块——这种『库而不是框架』的姿态在云原生时代比 Spring Boot 风格更灵活
- Polyglot 是 JVM 的隐藏优势:同一套 verticle 调度能跑 Java / Kotlin / JS,团队语言异构不用换运行时
- Reactive 不止是 API 风格,是整套思维转向:异常处理、调试、超时、背压都要重新学,省下来的资源换来更陡的认知成本
延伸阅读
- 官方文档:vertx.io/docs(每个模块独立 manual,配大量代码片段)
- GitHub 仓库:github.com/eclipse-vertx/vert.x
- Tim Fox 早期 JavaOne 演讲『Vert.x: Polyglot Async App Platform』(讲设计取舍)
- Julien Viet(4.x 主 maintainer)系列博客(vertx.io/blog)
- quarkus —— RedHat 云原生框架,底层 reactive 引擎就是 Vert.x
- Reactive Manifesto(reactivemanifesto.org)—— 理解 Vert.x 设计原则的源头
关联
- spring-boot —— Java 后端事实标准,对照看『阻塞 servlet vs 非阻塞 event loop』两条路
- quarkus —— Vert.x 的下游主要消费者,云原生场景的现成上层封装
- express —— Node.js 同款 event loop 思路,Vert.x 的 vertx-web Router API 体感很像
- fastify —— 同为 Node 高性能框架,对照看 schema-first vs polyglot toolkit 的取舍
- axum —— Rust 异步 web 框架,看类型驱动 vs 消息总线两种 reactive 风格
- netty —— Vert.x 底层 IO 库,理解 Vert.x 必经一站
- kubernetes —— Vert.x 集群部署主流落点,自带 vertx-k8s-discovery 模块
反向链接
- axum —— axum — 用 Rust 类型系统当『路由参数表』的 Web 框架
- dropwizard —— Dropwizard — Java 微服务的”开箱即用 12-factor 起步包”
- express —— Express — Node.js 最经典的 Web 框架
- fastify —— Fastify — 让 schema 替你写校验和序列化的 Node.js 框架
- ktor —— Ktor — 用 Kotlin DSL 拼出来的异步 Web 框架
- kubernetes —— Kubernetes — 容器编排平台
- quarkus —— Quarkus — 让 Java 启动比 Node 还快的云原生框架
- spring-boot —— Spring Boot — 用 Auto-configuration 把 Java 后端从 XML 地狱里救出来的事实标准框架