SpringBoot 2.x (14):WebFlux響應式編程

響應式編程生活案例:java

傳統形式:react

一羣人去餐廳吃飯,顧客1找服務員點餐,服務員把訂單交給後臺廚師,而後服務員等待,web

當後臺廚師作好飯,交給服務員,通過服務員再交給顧客1,依此類推,該服務員再招待顧客2。redis

服務員能夠理解爲服務器,服務器越多,可處理的顧客請求越多spring

響應式編程:數據庫

服務員記住到顧客1的要求,交給後臺廚師,再記住顧客2的要求,交給後臺廚師,依此類推編程

當廚師作好顧客1的飯,告知服務員,而後服務員把飯送到顧客1;設計模式

當廚師作好顧客2的飯,告知服務員,而後服務員把飯送到顧客2,依此類推數組

 

一系列的事件稱爲流,異步非阻塞,觀察者的設計模式服務器

 

代碼案例:

傳統:

int b=2;
int c=3
int a=b+c //a被賦值後,b和c的改變不會影響a
b=5;

 

響應式編程:

int b=2;
int c=3
int a=b+c 
b=5;//此時a變化爲8,a會根據b、c的變化而變化

 

SpringBoot2.x的響應式編程基於Spring5;

而Spring5的響應式編程又基於Reactor和Netty、Spring WebFlux替代Spring MVC

 

響應式編程最大的核心是非阻塞,即後臺的每一步每一段都要作到非阻塞

好比使用MySQL做爲數據庫,因爲MySQL不提供響應式編程,因此會阻塞

所以響應式編程不該採用MySQL,應該使用非阻塞的NoSQL

 

Spring WebFlux有兩種風格:基於功能和基於註解的。基於註解很是接近Spring MVC模型,如如下示例所示:

            @RestController 
            @RequestMapping(「/ users」)
             public  class MyRestController {

                @GetMapping(「/ {user}」)
                 public Mono <User> getUser( @PathVariable Long user){
                     // ...
                }

                @GetMapping(「/ {user} / customers」)
                 public Flux <Customer> getUserCustomers( @PathVariable Long user){
                     // ...
                }

                @DeleteMapping(「/ {user}」)
                 public Mono <User> deleteUser( @PathVariable Long user){
                     // ...
                }

            }

第二種: 路由配置與請求的實際處理分開

            @Configuration
             public  class RoutingConfiguration {

                @Bean
                 public RouterFunction <ServerResponse> monoRouterFunction(UserHandler userHandler){
                     return route(GET( 「/ {user}」).and(accept(APPLICATION_JSON)),userHandler :: getUser)
                            .andRoute(GET(「/ {user} / customers」).and(accept(APPLICATION_JSON)),userHandler :: getUserCustomers)
                            .andRoute(DELETE(「/ {user}」).and(accept(APPLICATION_JSON)),userHandler :: deleteUser);
                }

            }

            @Component
            public class UserHandler {

                public Mono <ServerResponse> getUser(ServerRequest request){
                     // ...
                }

                public Mono <ServerResponse> getUserCustomers(ServerRequest request){
                     // ...
                }

                public Mono <ServerResponse> deleteUser(ServerRequest request){
                     // ...
                }
            }

Spring WebFlux應用程序不嚴格依賴於Servlet API,所以它們不能做爲war文件部署,也不能使用src/main/webapp目錄

能夠整合多個模板引擎,除了REST外,您還可使用Spring WebFlux提供動態HTML內容

Spring WebFlux支持各類模板技術,包括Thymeleaf,FreeMarker

 

簡單的實戰:

依賴

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

自動生成的SpringBoot項目還會有一個test依賴,可選

        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>

簡單的Controller:

package org.dreamtech.webflux.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import reactor.core.publisher.Mono;

@RestController
public class TestController {
    @GetMapping("/test")
    public Mono<String> test() {
        return Mono.just("hello world");
    }
}

訪問http://localhost:8080/test,顯示hello world說明成功

 

這裏使用到了Mono,後邊還會用到Flux,他們的實現很複雜,但能夠簡單地理解:

User、List<User>

1)簡單業務而言:和其餘普通對象差異不大,複雜請求業務,就能夠提高性能
2)通俗理解:
Mono 表示的是包含 0 或者 1 個元素的異步序列
mono->單一對象 User
例如從redis根據用戶ID查到惟一的用戶,而後進行返回Mono<User>

Flux 表示的是包含 0 到 N 個元素的異步序列
flux->數組列表對象 List<User>
例如從redis根據條件:性別爲男性的用戶進行查找,而後返回Flux<User>
3)Flux 和 Mono 之間能夠進行轉換

 

進一步的使用

對User實體類實現增刪改查功能:

package org.dreamtech.webflux.domain;

public class User {
    private String id;
    private String name;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public User(String id, String name) {
        super();
        this.id = id;
        this.name = name;
    }

}

Service:

package org.dreamtech.webflux.service;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.dreamtech.webflux.domain.User;
import org.springframework.stereotype.Service;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Service
public class UserService {
    // 使用Map模擬數據庫
    private static final Map<String, User> dataMap = new HashMap<String, User>();
    static {
        dataMap.put("1", new User("1", "admin"));
        dataMap.put("2", new User("2", "John"));
        dataMap.put("3", new User("3", "Rose"));
        dataMap.put("4", new User("4", "James"));
        dataMap.put("5", new User("5", "Bryant"));
    }

    /**
     * 返回數據庫的全部用戶信息
     * 
     * @return
     */
    public Flux<User> list() {
        Collection<User> list = UserService.dataMap.values();
        return Flux.fromIterable(list);
    }

    /**
     * 根據用戶ID返回用戶信息
     * 
     * @param id 用戶ID
     * @return
     */
    public Mono<User> getById(final String id) {
        return Mono.justOrEmpty(UserService.dataMap.get(id));
    }

    /**
     * 根據用戶ID刪除用戶
     * 
     * @param id 用戶ID
     * @return
     */
    public Mono<User> delete(final String id) {
        return Mono.justOrEmpty(UserService.dataMap.remove(id));
    }
}

Controller:

package org.dreamtech.webflux.controller;

import org.dreamtech.webflux.domain.User;
import org.dreamtech.webflux.service.UserService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@RestController
public class UserController {
    private final UserService userService;

    public UserController(final UserService userService) {
        this.userService = userService;
    }

    /**
     * 根據ID查找用戶
     * 
     * @param id 用戶ID
     * @return
     */
    @GetMapping("/find")
    public Mono<User> findById(final String id) {
        return userService.getById(id);
    }

    /**
     * 得到用戶列表
     * 
     * @return
     */
    @GetMapping("/list")
    public Flux<User> list() {
        return userService.list();
    }

    /**
     * 根據ID刪除用戶
     * 
     * @param id 用戶ID
     * @return
     */
    @GetMapping("/delete")
    public Mono<User> delete(final String id) {
        return userService.delete(id);
    }
}

訪問定義的三個API,發現和SpringMVC基本沒有區別

因此,對返回進行延遲處理:

    @GetMapping("/list")
    public Flux<User> list() {
        return userService.list().delayElements(Duration.ofSeconds(3));
    }

只是這些設置的話,等待3*list.size秒後所有返回,要突出流的特色,須要進行配置:

    @GetMapping(value = "/list", produces = MediaType.APPLICATION_STREAM_JSON_VALUE)
    public Flux<User> list() {
        return userService.list().delayElements(Duration.ofSeconds(3));
    }

這時候訪問,能夠發現每過3秒返回一個對象信息

 

使用WebClient客戶端進行測試:

package org.dreamtech.webflux;

import org.junit.Test;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;

import reactor.core.publisher.Mono;

public class WebClientTest {
    @Test
    public void test() {
        Mono<String> bodyMono = WebClient.create().get().uri("http://localhost:8080/find?id=3")
                .accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(String.class);
        System.out.println(bodyMono.block());
    }
}
相關文章
相關標籤/搜索