Reactor 2024.0 发布,支持 HTTP/3
HTTP/3 是 Hypertext Transfer Protocol(超文本传输协议,即 HTTP)的最新主要版本,其规范已于 2022 年 6 月定稿。该版本旨在提高性能、可靠性和安全性。与之前的版本不同,HTTP/3 使用 QUIC 而不是 TCP 作为传输层。QUIC 是一种基于 UDP 的多路复用安全传输协议,内置 TLS 1.3 加密,默认情况下 QUIC 是加密的。
要进一步了解 HTTP/3 的性能和优势,可以查看 《什么是 HTTP/3》。
有关浏览器采用情况的信息,请参阅《检查 HTTP/3 使用情况》,其中还提供了不同浏览器使用的 HTTP 版本的原始数据。
Reactor Netty 1.2(Reactor 2024.0 发布系列的一部分)添加了对 HTTP/3 的实验性支持。通过这个新版本的 Reactor Netty,你可以配置你的 Spring Boot 应用和 Spring Cloud Gateway 来支持 HTTP/3。
下面让我们看看如何配置 HTTP/3 支持。
配置 Reactor BOM Version
Spring Boot 3.4 默认集成了 Reactor 2024.0 发布系列! 如果你运行的是旧版本的 Spring Boot,你可以将 Reactor BOM 升级到 2024.0(截至本文撰写时,2024.0.0 是最新版本),尝试使用这项新功能。
Maven:
<properties>
    <reactor-bom.version>2024.0.0</reactor-bom.version>
</properties>
Gradle:
ext['reactor-bom.version'] = '2024.0.0'
配置 Netty HTTP3 Codec
你还需要添加 Netty HTTP3 Codec(编解码器)的运行时依赖(截至本文撰写时,最新版本是 0.0.28.Final)。
Maven:
<dependencies>
    <dependency>
        <groupId>io.netty.incubator</groupId>
        <artifactId>netty-incubator-codec-http3</artifactId>
        <version>0.0.28.Final</version>
        <scope>runtime</scope>
    </dependency>
</dependencies>
Gradle:
dependencies {
    runtimeOnly 'io.netty.incubator:netty-incubator-codec-http3:0.0.28.Final'
}
服务器
配置 SSL Bundle
首先需要提供一个 SSL Bundle,其中包含应用所需的配置:Keystore、密码等。
application.properties:
spring.ssl.bundle.jks.server-http3.key.alias=http3
spring.ssl.bundle.jks.server-http3.keystore.location=...
spring.ssl.bundle.jks.server-http3.keystore.password=...
# ...
application.yml
spring:
  ssl:
    bundle:
      jks:
        server-http3:
          key:
            alias: http3
          keystore:
            location: ...
            password: ...
# ...
配置嵌入式服务器
Spring Boot 提供了配置嵌入式服务器的能力。
你可以声明一个 WebServerFactoryCustomizer 组件,并访问 Server Factory。为了启用 HTTP/3 支持,你需要:
- 指定 HTTP/3 协议:默认情况下,Reactor Netty 配置为支持 HTTP/1.1,因此需要进行更改。
 - 指定 HTTP/3 设置:默认情况下,Reactor Netty 不提供任何设置,因为这些设置与应用程序密切相关,所以你必须对它们进行配置,如空闲超时、最大流等。
 - 指定 HTTP/3 SSLContext。
 
之前配置的 SSL Bundle 可以通过其名称从 Server Factory 获取,方法是 factory.getSslBundles().getBundle("server-http3"),然后用其配置 Http3SslContextSpec。
@Component
class Http3NettyWebServerCustomizer implements WebServerFactoryCustomizer<NettyReactiveWebServerFactory> {
    @Override
    public void customize(NettyReactiveWebServerFactory factory) {
        factory.addServerCustomizers(server -> {
            SslBundle sslBundle = factory.getSslBundles().getBundle("server-http3");
            Http3SslContextSpec sslContextSpec =
                Http3SslContextSpec.forServer(sslBundle.getManagers().getKeyManagerFactory(), sslBundle.getKey().getPassword());
            return server
                    // 配置 HTTP/3 协议
                    .protocol(HttpProtocol.HTTP3)
                    // 配置 HTTP/3 SslContext
                    .secure(spec -> spec.sslContext(sslContextSpec))
                    // 配置 HTTP/3 的设置
                    .http3Settings(spec -> spec
                            .idleTimeout(Duration.ofSeconds(5))
                            .maxData(10_000_000)
                            .maxStreamDataBidirectionalRemote(1_000_000)
                            .maxStreamsBidirectional(100));
        });
    }
}
Spring Cloud Gateway 也使用同样的方法来实现。
REST Controller
最后需要添加的是一个简单的 hello REST Controller。REST Controller 不需要任何特定的 HTTP/3 配置!
@RestController
class Http3Controller {
    @GetMapping("/hello")
    String hello() {
        return "Hello HTTP/3!";
    }
}
现在,可以执行第一个 HTTP/3 请求了:
curl --http3 https://localhost:8443/hello --verbose
* Connected to localhost (::1) port 8443
* using HTTP/3
* [HTTP/3] [0] OPENED stream for https://localhost:8443/hello
* [HTTP/3] [0] [:method: GET]
* [HTTP/3] [0] [:scheme: https]
* [HTTP/3] [0] [:authority: localhost:8443]
* [HTTP/3] [0] [:path: /hello]
* [HTTP/3] [0] [user-agent: curl]
* [HTTP/3] [0] [accept: */*]
> GET /hello HTTP/3
> Host: localhost:8443
> User-Agent: curl
> Accept: */*
> 
* Request completely sent off
< HTTP/3 200 
< content-type: text/plain;charset=UTF-8
< content-length: 13
< 
* Connection #0 to host localhost left intact
Hello HTTP/3!
spring-webflux-http3 仓库包含了完整的示例。
客户端
为客户端配置 HTTP/3 支持与配置服务器的方法类似!
你需要:
import reactor.netty.http.client.HttpClient;
HttpClient client = HttpClient.create()
        // 指定 HTTP/3 协议
        .protocol(HttpProtocol.HTTP3)
        // 指定 HTTP/3 设置
        .http3Settings(spec -> spec
                .idleTimeout(Duration.ofSeconds(5))
                .maxData(10_000_000)
                .maxStreamDataBidirectionalLocal(1_000_000));
默认情况下,客户端使用 Reactor Netty 提供的标准 HTTP/3 SSLContext。不过,如果需要更具体的配置:Truststore、密码等,可以像为服务器准备 SSL Bundle 一样准备 SSL Bundle,并配置 Http3SslContextSpec。
SslBundle sslBundle = factory.getSslBundles().getBundle("client-http3");
Http3SslContextSpec sslContextSpec = Http3SslContextSpec.forClient()
        // 配置 TrustStore
        .configure(...);
HttpClient client = HttpClient.create()
        ...
        // 配置 HTTP/3 SslContext
        .secure(spec -> spec.sslContext(sslContextSpec));
WebClient
你可以使用 ReactorClientHttpConnector 配置底层的 Reactor Netty HttpClient。
@Bean
WebClient http3WebClient(WebClient.Builder builder) {
    HttpClient client = ...;
    return builder.clientConnector(new ReactorClientHttpConnector(client)).build();
}
REST Controller
你可以创建一个简单的 REST Controller,利用新的 HTTP/3 配置进行远程调用。REST Controller 不需要任何特定的 HTTP/3 配置!
@RestController
class Http3Controller {
    private final WebClient http3WebClient;
    Http3Controller(WebClient http3WebClient) {
        this.http3WebClient = http3WebClient;
    }
    @GetMapping("/remote")
    Mono<String> remote() {
        return http3WebClient
                .get()
                .uri("https://projectreactor.io/")
                .retrieve()
                .bodyToMono(String.class);
    }
}
spring-webflux-http3 仓库库包含了完整的示例。
RestClient
你可以使用 ReactorNettyClientRequestFactory 配置底层的 Reactor Netty HttpClient。
@Bean
RestClient http3RestClient(RestClient.Builder builder) {
    HttpClient client = ...;
    return builder.requestFactory(new ReactorNettyClientRequestFactory(client)).build();
}
REST Controller
你可以创建一个简单的 REST Controller,利用新的 HTTP/3 配置进行远程调用。REST Controller 不需要任何特定的 HTTP/3 配置!
@RestController
class Http3Controller {
    private final RestClient http3RestClient;
    Http3Controller(RestClient http3RestClient) {
        this.http3RestClient = http3RestClient;
    }
    @GetMapping("/remote")
    String remote() {
        return http3RestClient
                .get()
                .uri("https://projectreactor.io/")
                .retrieve()
                .body(String.class);
    }
}
spring-webmvc-http3 仓库库包含了完整的示例。
现在,你可以进行 HTTP/3 远程调用了:
curl --http3 https://localhost:8443/remote --verbose
* Connected to localhost (::1) port 8443
* using HTTP/3
* [HTTP/3] [0] OPENED stream for https://localhost:8443/remote
* [HTTP/3] [0] [:method: GET]
* [HTTP/3] [0] [:scheme: https]
* [HTTP/3] [0] [:authority: localhost:8443]
* [HTTP/3] [0] [:path: /remote]
* [HTTP/3] [0] [user-agent: curl/8]
* [HTTP/3] [0] [accept: */*]
> GET /remote HTTP/3
> Host: localhost:8443
> User-Agent: curl/8
> Accept: */*
> 
* Request completely sent off
< HTTP/3 200 
< content-type: text/plain;charset=UTF-8
< content-length: 17138
...
Spring Cloud Gateway
你可以使用 Spring Cloud Gateway 中的 HttpClientCustomizer 配置底层的 Reactor Netty HttpClient。要使用该 customizer,需要在 Spring Cloud Gateway 配置中注册它。
@Configuration
class GatewayConfiguration {
    @Bean
    HttpClientCustomizer http3HttpClientCustomizer() {
        return httpClient ->
                httpClient
                        // 配置 HTTP/3 协议
                        .protocol(HttpProtocol.HTTP3)
                        // 配置 HTTP/3 设置
                        .http3Settings(spec -> spec.idleTimeout(Duration.ofSeconds(5))
                                .maxData(10_000_000)
                                .maxStreamDataBidirectionalLocal(1_000_000));
    }
}
spring-cloud-gateway-http3 仓库库包含了完整的示例。
最后,你可以在官方的 GitHub / Twitter 上提出反馈意见!
Ref:https://spring.io/blog/2024/11/26/http3-in-reactor-2024