1. 程式人生 > 實用技巧 >Spring WebFlux(一)

Spring WebFlux(一)

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");
  }
}