Files
rikako-note/spring/webflux/spring webflux.md
2025-03-10 12:56:28 +08:00

183 lines
8.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Spring Webflux
## Concept
### 核心机制
#### reactive
reactive代表基于“事件响应”的编程模型
#### back pressure
在spring webflux中`back pressure`为反应式编程的核心机制,用于协调生产者和消费者之间的速率差异,令系统在高负载或资源受限的情况下仍能稳定运行。
- 同步场景在同步场景下阻塞式调用是一种天然的back pressure形式调用方会阻塞并等待直到被调用方执行完成
- 非阻塞场景:在非阻塞的代码中,需要关注事件速率,生产者产生事件的速率不能压过消费者消费的速率
##### reactive stream
reactive stream为一个小型规范定义了异步组件和back pressure交互的规范。reactive stream的主要用途是让Subscriber控制publisher产生数据的速率。
### 编程模型
`spring-web` module包含Spring webflux的响应式基础包括若夏内容
- http抽象
- 对支持server的reactive stream adapters
- codecs
- 核心WebHandler API其与servlet api兼容
Spring webflux提供了如下两种编程模型
- `Annotated Controllers`: 和spring mvc一致基于相同的注解。`spring mvc``webflux controllers`都支持`reactive return type`,因此,很难区分`spring mvc``webflux controllers`
- `Functional Endpoint`基于lambda的、轻量的函数编程模型。其可以被看做是一个`支持对请求进行route和handle`的工具集合。
### 并发模型
#### spring mvc
`spring mvc`通常为servlet应用其假设当前线程可能会被阻塞例如远程调用等会阻塞当前线程。为了减少处理请求时阻塞所带来的影响servlet容器会使用包含大量线程的线程池。
#### webflux
对于`webflux`通常为non-blocking server其会假设应用并不会阻塞故而非阻塞的server可以使用一个线程数较少且固定的线程池event loop workers来处理请求。
#### 调用阻塞api
当想要在webflux中使用阻塞api时可以按如下方式进行使用。`RxJava``Reactive`都支持`publushOn`操作,其支持在另一个线程中`继续执行`
## Reactive Core
`spring-web`对于reactive web应用具有如下支持
- 对于server request的处理拥有如下两种级别的支持
- `HttpHandler` 基于`non-blocking I/O``Reactive Stream back pressure`,适配`Reactor Netty, Undertow, Tomcat, Jetty以及任一Servlet Container
- `WebHandler`稍高级别的、通用的web api用于请求处理在此基础上构建基于`annotated controllers``functional endpoints`的编程模型
- 对于client端`ClientHttpConnector`用于发送http请求同时兼容`非阻塞io和Reactive Stream back pressure`
- codecs用于客户端和服务端的序列化和反序列化
### HttpHandler
HttpHandler中只通过一个单独的方法来处理请求和相应其对不同的http server api进行了最小化的抽象。
下表描述了支持的api
<table class="tableblock frame-all grid-all stripes-odd stretch">
<colgroup>
<col style="width: 20%;">
<col style="width: 40%;">
<col style="width: 40%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Server name</th>
<th class="tableblock halign-left valign-top">Server API used</th>
<th class="tableblock halign-left valign-top">Reactive Streams support</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Netty</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Netty API</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://github.com/reactor/reactor-netty" class="external" target="_blank">Reactor Netty</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Undertow</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Undertow API</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">spring-web: Undertow to Reactive Streams bridge</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Tomcat</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Servlet non-blocking I/O; Tomcat API to read and write ByteBuffers vs byte[]</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">spring-web: Servlet non-blocking I/O to Reactive Streams bridge</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Jetty</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Servlet non-blocking I/O; Jetty API to write ByteBuffers vs byte[]</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">spring-web: Servlet non-blocking I/O to Reactive Streams bridge</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Servlet container</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Servlet non-blocking I/O</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">spring-web: Servlet non-blocking I/O to Reactive Streams bridge</p></td>
</tr>
</tbody>
</table>
下表中描述了server依赖
<table class="tableblock frame-all grid-all stripes-odd stretch">
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 33.3333%;">
<col style="width: 33.3334%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Server name</th>
<th class="tableblock halign-left valign-top">Group id</th>
<th class="tableblock halign-left valign-top">Artifact name</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Reactor Netty</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">io.projectreactor.netty</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">reactor-netty</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Undertow</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">io.undertow</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">undertow-core</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Tomcat</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">org.apache.tomcat.embed</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">tomcat-embed-core</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Jetty</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">org.eclipse.jetty</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">jetty-server, jetty-servlet</p></td>
</tr>
</tbody>
</table>
#### server api adapters
如下示例展示了如何使用`HttpHandler` Adapters来适配不同的Server Api
##### Reactor Netty
```java
HttpHandler handler = ...
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
HttpServer.create().host(host).port(port).handle(adapter).bindNow();
```
##### Undertow
```java
HttpHandler handler = ...
UndertowHttpHandlerAdapter adapter = new UndertowHttpHandlerAdapter(handler);
Undertow server = Undertow.builder().addHttpListener(port, host).setHandler(adapter).build();
server.start();
```
##### Tomcat
```java
HttpHandler handler = ...
Servlet servlet = new TomcatHttpHandlerAdapter(handler);
Tomcat server = new Tomcat();
File base = new File(System.getProperty("java.io.tmpdir"));
Context rootContext = server.addContext("", base.getAbsolutePath());
Tomcat.addServlet(rootContext, "main", servlet);
rootContext.addServletMappingDecoded("/", "main");
server.setHost(host);
server.setPort(port);
server.start();
```
##### Jetty
```java
HttpHandler handler = ...
Servlet servlet = new JettyHttpHandlerAdapter(handler);
Server server = new Server();
ServletContextHandler contextHandler = new ServletContextHandler(server, "");
contextHandler.addServlet(new ServletHolder(servlet), "/");
contextHandler.start();
ServerConnector connector = new ServerConnector(server);
connector.setHost(host);
connector.setPort(port);
server.addConnector(connector);
server.start();
```
### WebHandler API
`org.springframework.web.server` package基于`HttpHandler`构建提供了通用的Web API。Web Api由多个`WebException`, 多个`WebFilter`, 一个`WebHandler`组件构成组成了一个chain。