Reactive
反應系統具有某些特性,使其非常適合低延遲,高吞吐量的工作負載。Project Reactor 和 Spring 產品組合一起使開發人員能夠構建可響應,有彈性,有彈性和消息驅動的企業級反應系統。
什么是 reactive processing?
響應式處理是使開發人員能夠構建可處理背壓(流控制)的非阻塞異步應用程序的范例。
為什么使用 reactive processing?
反應系統更好地利用了現代處理器。同樣,在反應式編程中包含背壓可確保解耦組件之間的彈性更好。
Project Reactor
Project Reactor 是一個完全無阻塞的基礎,其中包括背壓支持。它是Spring生態系統中反應式堆的基礎,並且在諸如Spring WebFlux,Spring Data和Spring Cloud Gateway等項目中得到了突出體現。
Reactive Microservices
開發人員從阻塞代碼轉移到非阻塞代碼的主要原因之一是效率。反應性代碼用更少的資源就能完成更多工作。 Project Reactor 和 Spring WebFlux 使開發人員可以利用下一代多核處理器來處理潛在的大量並發連接。通過反應式處理,您可以使用更少的微服務實例來滿足更多並發用戶的需求。
Reactive Microservices With Spring Boot
Spring產品組合提供了兩個並行技術棧。一種基於帶有Spring MVC和Spring Data結構的Servlet API。另一個是完全響應式堆棧,該技術棧利用了 Spring WebFlux 和 Spring Data 的響應式存儲庫。在這兩種情況下,Spring Security 都為兩個技術棧都提供了支持。
Integration with common technologies
以反應方式訪問和處理數據很重要。 MongoDB,Redis和Cassandra在Spring Data中都具有原生響應式支持。許多關系數據庫(Postgres,Microsoft SQL Server,MySQL,H2和Google Spanner)都通過R2DBC提供了響應式支持。在消息傳遞領域,Spring Cloud Stream還支持對 RabbitMQ 和 Kafka 等平台的反應式訪問。
構建一個反應式 RESTful web 服務
構建環境
- Java 1.8 以上
- Maven 3.2 或 Gradle 4 以上
操作流程
創建一個 Maven 項目,添加 Maven 依賴
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.4.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>2.4.1</version>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<version>3.4.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
創建一個 WebFlux Handler(類似之前Service)
package com.holddie.flux.hello;
import reactor.core.publisher.Mono;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
@Component
public class GreetingHandler {
public Mono<ServerResponse> hello(ServerRequest request) {
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue("hello " + request.queryParam("name").get()));
}
}
創建一個 Router(類似 Controller 路由注冊)
@Configuration
public class GreetingRouter {
@Bean
public RouterFunction<ServerResponse> route(GreetingHandler greetingHandler) {
return RouterFunctions.route(RequestPredicates.GET("/hello")
.and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), greetingHandler::hello);
}
}
創建一個 WebClient
public class GreetingWebClient {
private WebClient client = WebClient.create("http://localhost:8080");
private Mono<ClientResponse> result = client.get()
.uri("/hello?name=thomas")
.accept(MediaType.APPLICATION_JSON)
.exchange();
public String getResult() {
return ">> result = " + result.flatMap(res -> res.bodyToMono(String.class)).block();
}
}
創建一個 SpringBoot 啟動類
@SpringBootApplication
public class HelloFluxApplication {
public static void main(String[] args) {
SpringApplication.run(HelloFluxApplication.class);
GreetingWebClient greetingWebClient = new GreetingWebClient();
System.out.println(greetingWebClient.getResult());
}
}
編寫測試類
測試訪問 http://localhost:8080/hello?name=thomas
返回 hello thomas
。
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.reactive.server.WebTestClient;
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class HelloFluxApplicationTest {
@Autowired
private WebTestClient webTestClient;
@Test
public void testHello() {
webTestClient.get().uri("hello?name=thomas")
.accept(MediaType.APPLICATION_JSON)
.exchange().expectStatus().isOk()
.expectBody(String.class).isEqualTo("hello thomas");
}
}