Níma 是一个基于 Java 19(目前是早期访问)的服务器实现,专为 Java 虚拟线程(Project Loom 的产品)而设计。
Helidon 4.0.0-ALPHA1 现在与我们全新的 Helidon Níma 一起发布,提供基于虚拟线程的 Web 服务器。对于那些对最新 Java 技术感兴趣的人来说,这是一个早期访问版本,但它还不适合生产使用!
要试用可用于生产的 Helidon,请查看我们的最新版本Helidon 3.0.0。
在 Alpha 版本中,我们提供了以下协议的实现:
- 带有流水线的 HTTP/1.1 — 服务器和客户端
- HTTP/2 服务器(原型,已知问题)
- gRPC 服务器(原型,已知问题)
- WebSocket 服务器(原型)
线程
该实现使用虚拟线程,其设计和实现是为了提供一个恒定的、低开销的、高并发的服务器,同时保持一个阻塞线程模型。这让你写的代码不会因为反应式编程中经常遇到的问题而变得复杂,比如说。
套接字监听器
- 套接字监听器是平台线程(数量非常少,每个打开的服务器套接字都有一个)。
HTTP/1.1
- 1个虚拟线程来处理连接(包括路由)
- 1个虚拟线程用于在该连接上进行写操作(可以被禁用,因此写操作发生在连接处理线程上)
- 单个连接的所有请求都由连接处理程序来处理
HTTP/2.2:
- 1个虚拟线程来处理连接
- 1个虚拟线程处理该连接的写操作(可以禁用,以便写操作发生在连接处理程序线程上)
- 每个HTTP/2流有一个虚拟线程(包括路由)。
虚拟线程执行器服务使用无边界的执行器。
协议
以下协议在我们的Alpha版本中已经实现。
- 具有可扩展升级机制的HTTP/1.1
- HTTP/1.1 WebSocket的升级实现
- HTTP/1.1到HTTP/2明文(h2c)的升级实现
- 具有可扩展的 "子协议 "机制的HTTP/2
- HTTP/2 gRPC子协议的实现
- 对其他TCP协议(包括非HTTP)的可扩展性
- 对任何协议的服务器端TLS支持
- 对任何协议的相互TLS支持
- 可扩展的应用层协议协商(ALPN),由HTTP/2(h2)使用
路由
同一个 Web 服务器可用于路由到多个协议(例如,您可以拥有一个服务于 HTTP/1.1、HTTP/2、WebSocket 和 gRPC 的端口)。默认情况下实现 HTTP/1.1 路由(因为其他协议从它升级);其他协议是可以添加的单独模块。
- HTTP 路由与版本无关——相同的路由可用于 HTTP/1.1 和 HTTP/2
- 支持特定协议的路由——只服务于 HTTP/2 的路由是可能的
- gRPC 路由(一元、服务器流式传输、客户端流式传输、双向)
- WebSocket 路由(目前不实现子协议)
特征
实现了以下功能,可以尝试:
- 跟踪支持——使用现有的 Helidon 跟踪实现,例如 Jaeger 或 Zipkin
- 静态内容支持——来自类路径或文件系统
- CORS 支持
- 访问日志支持
- 可观察性端点(健康、应用程序信息、配置)
- 容错(隔板、断路器、重试和超时功能)
- HTTP/1.1 客户端
- 测试支持
阻塞式与反应式
让我们比较一下Níma(阻塞式)和Helidon SE(反应式)在同一任务中的实现。
我们需要建立每个框架的基本规则。
- 反应式--你不能阻塞请求的线程,这是通过反应式流API支持的,比如Helidon的Multiand Single
- 阻塞式--你不能异步地完成响应(例如,响应必须从发出请求的同一线程发出)
注意:在这两种情况下,你都不应该 "阻塞 "线程。阻碍是对线程的长期、充分的利用。
- 在一个反应式框架中,这将消耗一个事件循环线程,有效地停止了服务器。
- 在阻塞式(Níma)中,这可能会导致 "钉子线程 "的问题。
在这两种情况下,可以通过使用平台线程将重载卸载到专门的执行器服务来解决。
堵塞:
private void one(ServerRequest req, ServerResponse res) { |
反应式:
private void one(ServerRequest req, ServerResponse res) { |
我们可以看到,使用阻塞代码,我们可以获取响应并发送它。如果发生任何事情,默认异常处理程序将正确处理异常,或发送内部服务器错误。
另一方面,对于反应、响应式,我们必须使用响应式流并处理异常,否则异常会丢失。
阻塞代码的优点:
- 简单的异常处理
- 有意义的堆栈跟踪(包括线程转储)
- 易于调试
- 更少的脚手架
复杂案例
在此示例中,我们将并行调用另一个服务,将结果组合成一个响应。
我们期望从查询参数中获取并行请求的数量;我们将把它放在变量“count”中。此外,此处未显示对 InterruptedException 和 ExecutionException 的处理,即使必须这样做(在下一个 Alpha 版本中,处理程序将允许抛出已检查的异常)。
阻塞:
List<String> responses = new LinkedList<>(); |
反应性:
// create a dummy stream from numbers 0 to count |
尽管我们用响应式代码实现了相同的效果,但代码的可读性要差得多(并且很难正确编写)。
性能
我们主要关注的是性能。下面我们展示了与纯Netty(4.1.36.Final版--没有额外的功能,只有HTTP)上的非阻塞实现相比的当前数据(ALPHA-1版)。
这些是在一台机器上使用环回接口进行的相当简单的基准测试(例如,有一个已知的客户端和服务器进程的干扰,因为它们共享CPU)。然而,它为我们提供了一个快速的性能比较,看看我们是否可以与非阻塞的实现相比较。与任何性能测试一样,结果会因很多因素而不同(特别是对Linux环境如何进行性能优化)。
测试结果:
Nima可以实现与极简 Netty 服务器相当的性能,同时保持简单、易于使用的编程模型。
Performance results (requests/second)
详细点击标题