Vert.x 5.2 重磅新特性全景解读
置顶文章Vert.x 5.2 带来了众多令人兴奋的全新功能与架构演进。
本文将对 Vert.x 5.2 中支持的最重磅核心特性进行深度盘点。
基于 Event Bus 的 gRPC 流式调用
Vert.x 5.1 首次引入了 gRPC EventBus 传输通道,允许代码生成的 gRPC 服务直接通过 Vert.x Event Bus 事件总线进行通信——不过当时仅支持一元(Unary 请求/响应)调用模式。
Vert.x 5.2 将其扩展为完整的流式支持:现在你可以在事件总线上自由使用客户端流式(Client Streaming)、服务端流式(Server Streaming)以及双向全双工流式(Bidirectional Streaming)。
服务端流式实现
流式服务端接口通过 ReadStream 与 WriteStream 抽象进行实现:
ConversationalServiceGrpcService service = new ConversationalServiceGrpcService() {
@Override
protected void fullDuplexCall(ReadStream<StreamingOutputCallRequest> request,
WriteStream<StreamingOutputCallResponse> response) {
// 监听客户端流入的请求帧
request.handler(req -> {
// 模拟定时异步写回响应
vertx.setTimer(500L, t -> {
response.write(StreamingOutputCallResponse.newBuilder().build());
});
});
}
};
// 启动基于事件总线的 gRPC 服务端
Future<EventBusGrpcServer> fut = EventBusGrpcServer.server(vertx);
fut.onSuccess(server -> {
server.addService(service);
});客户端流式调用
客户端利用流式回调发送消息,并通过返回的 ReadStream 异步消费响应结果:
Future<EventBusGrpcClient> fut = EventBusGrpcClient.client(vertx);
fut.compose(client -> {
ConversationalServiceGrpcClient stub = ConversationalServiceGrpcClient.create(client);
return stub.fullDuplexCall((stream, err) -> {
// 写入请求数据流
stream.write(StreamingOutputCallRequest.newBuilder().build());
}).onSuccess(response -> {
// 监听服务端推送的响应流
response.handler(msg -> {
System.out.println("收到服务端响应数据");
});
});
});在官方实战仓库中可以查阅完整的 代码示例。
面向 HTTP 转码的 gRPC 服务端流式输出
HTTP 转码 (HTTP Transcoding) 能够将 gRPC 服务方法映射为 RESTful HTTP 端点,使得外部在无需集成 gRPC 专用客户端的情况下即可通过标准 HTTP 进行直接调用。Vert.x 5.2 进一步将该转码能力扩展至服务端流式 RPC 场景。
在 Protobuf 定义文件中为服务端流式方法声明 HTTP 绑定注解:
service ExampleService {
rpc ServerStreaming(Request) returns (stream Response) {
option (google.api.http) = {
get: "/v1/example/serverstreaming/{value}"
};
};
}服务端实现通过持续写出多个响应帧并最终结束数据流:
ExampleServiceGrpcService service = new ExampleServiceGrpcService() {
@Override
protected void serverStreaming(Request request, WriteStream<Response> response) {
for (int i = 0; i < 5; i++) {
response.write(Response.newBuilder().setValue(request.getValue() + " " + i).build());
}
// 结束响应流
response.end();
}
};
GrpcServer rpcServer = GrpcServer.server(vertx);
rpcServer.addService(service);随后只需使用常规 HTTP 客户端工具(如 curl)即可发起流式消费:
> curl http://localhost:8080/v1/example/serverstreaming/Julien
[
{"value":"Julien 0"}
,{"value":"Julien 1"}
,{"value":"Julien 2"}
,{"value":"Julien 3"}
,{"value":"Julien 4"}
]返回的数据格式会通过 Accept 请求头进行动态协商:
| Accept 头参数 | 响应格式 |
|---|---|
application/json | JSON 数组 (JSON Array) |
application/x-ndjson | 换行符分隔 JSON (NDJSON) |
text/event-stream | 服务器发送事件 (SSE) |
可在示例仓库中查阅 HTTP 转码示例。
全面支持 HTTP QUERY 请求方法
Vert.x 5.2 正式引入了对 RFC 10008 标准所定义的 HTTP QUERY 方法 的支持。 QUERY 的语义与 GET 类似(安全且幂等),但允许携带请求体(Request Body),非常契合需要传递复杂查询参数但受限于 URL 长度限制的高级检索场景。
Vert.x 体系全栈均已对 QUERY 提供了原生支持:
- vertx-core —
HttpMethod.QUERY作为一等公民原生提供给客户端与服务端使用 - Vert.x Web — 路由规则可精准匹配 QUERY 请求方法;同时 Web 客户端缓存会将请求体指纹作为缓存键的一部分,准确区分相同路径下的 QUERY 与 GET 缓存
- Web Client — 客户端全面支持发送携带 Request Body 的 QUERY 请求
支持 HTTPS 前向代理
Vert.x 5.2 引入了全新的 ProxyType.HTTPS,支持通过 TLS 加密通道与前向代理服务器建立安全连接。此前,代理连接通常仅以明文方式进行(ProxyType.HTTP 或 ProxyType.SOCKS5),导致第一跳网络未受加密保护。采用 ProxyType.HTTPS 后,到代理服务器本身的传输链路将完全加密,这对于即使连入内部代理也必须穿越非受信网络的云原生多租户环境至关重要。
HttpClientOptions options = new HttpClientOptions()
.setProxyOptions(new ProxyOptions()
.setType(ProxyType.HTTPS)
.setHost("proxy.example.com")
.setPort(443)
.setSslOptions(new ClientSSLOptions()
.setTrustAll(false)
.setHostnameVerificationAlgorithm("HTTPS")));其代理语义与 ProxyType.HTTP 保持一致:明文目标采用普通转发,TLS 目标采用 CONNECT 隧道——唯一区别在于连接代理的第一步已被全程加密。对于 TLS 目标地址,这将形成两层嵌套 TLS 会话(一层面向代理,一层面向目标源站),并支持通过 ALPN 端到端无缝协商 HTTP/2 协议。
HTTPS 代理支持同时适用于 HTTP 客户端与 TCP 客户端。
SQL 客户端 JSON 支持增强
Vert.x 响应式 SQL 客户端在 5.2 中大幅增强了对关系型数据库原生 JSON 列的读写支持:
- MSSQL 原生 JSON 支持 — MSSQL 客户端现已完全支持 JSON 数据类型的读写交互
- Oracle JSON 支持 — Oracle 客户端完整适配了 Oracle 21c 引入的原生 JSON 列类型读写
全新熔断器构建器 (CircuitBreakerBuilder)
Vert.x 5.2 引入了 CircuitBreakerBuilder,提供了一种配置包含**不可变处理器(Immutable Handlers)**熔断器的新机制。
通过该 Builder 设置的事件处理器在构建期固化,后续无法被动态覆盖替换——若再次调用 setter 会显式抛出 IllegalStateException。这一设计不仅消除了对处理器字段进行并发同步加锁的性能开销,同时与既有的可变 API 保持了完全的向后兼容。
CircuitBreaker breaker = CircuitBreaker.builder("my-circuit-breaker", vertx)
.with(new CircuitBreakerOptions().setMaxFailures(5).setTimeout(2000))
// 熔断打开事件监听
.openHandler(v -> {
System.out.println("熔断器已触发打开!");
})
// 熔断恢复关闭事件监听
.closeHandler(v -> {
System.out.println("熔断器已恢复关闭。");
})
.build();未通过 Builder 固化的处理器依然保持动态可配置,因此基于原有 CircuitBreaker.create() 编写的存量代码无需做任何修改,继续稳定运行。


