天天看點

Spring Webflux學習記錄

現在Reactive在Java開發者中依然還是個霧裡看花的狀态,都知道不是銀彈,但是也都不知道在一般的業務場景它能不能用?能用到什麼程度?代價是什麼?是以為了破除迷信,近距離了解這項技術,從Spring Webflux開始學習,關鍵心得記錄如下。

異步思維

首先全異步程式設計是Reactive的核心,雖然一直作業務産品開發,但是對多線程操作也不陌生,直覺感覺上即使是全異步程式設計也不會有太大的不适應。但是,全異步真的是需要另一套代碼習慣。

下面這段代碼,按照我一開始的了解應該是要輸出都能輸出,但是事實上直接就報了“java.io.IOException: Pipe closed”,也就是說由于使用了JDK8 Closeable方式的try catch寫法,導緻在實際寫入前就在主線程被關閉。

在這裡傳統的優雅的寫法成為了全異步化程式設計模型的“陋習”,不得不在onFinally或者後續的阻塞場景寫一些樣闆代碼将其close。同時這裡也暴露了另一個問題,那就是在傳統開發模式和Reactive混合使用時,很容易由于前置的同步代碼潛在的預期語義,導緻異步化後出現各種難以捉摸的異常,并且如果沒寫doOnError還會丢失異常資訊。

@RestController
@RequestMapping("/test")
public class GreetingHandler {

    @GetMapping("/hello")
    public Flux<String> hello(ServerHttpRequest request) {
        return Flux.just("a", "b", "c");
    }
}

public static void main(String[] args) throws Exception {
    String url = "http://localhost:8080/test/hello";
    Flux<DataBuffer> response = WebClient.create()
        .get().uri(url).exchange()
        .flatMapMany(b -> b.bodyToFlux(DataBuffer.class));

    try(PipedOutputStream outputStream = new PipedOutputStream();
        PipedInputStream inputStream = new PipedInputStream(outputStream)) {
        DataBufferUtils.write(response, outputStream)
            .doFinally(s -> {
                System.out.println("doFinally: " + s.toString() + Thread.currentThread().getId());
                try {
                    System.out.println(inputStream.available());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            })
            .doOnNext(dataBuffer -> {
                System.out.println("doOnNext" + Thread.currentThread().getId());
            })
            .doOnError(t -> {
                logger.error("", t);
            })
            .subscribe(DataBufferUtils.releaseConsumer());
    } catch (Exception e) {
        logger.error("", e);
    }
}           

用戶端限制

Webflux将Controller的傳回固定成另Mono和Flux兩種,通過使用浏覽器接收到的和普通接口無二,那Webflux真的不限制用戶端類型嗎?

了解這個問題最好的辦法一是看源碼,其次就是抓包看一下通信協定。本次使用Wireshark抓包,截圖如下。從服務端傳回可以看出發送了三次資料,是以說明FLux接口會分成多個chunk傳回用戶端,同時整體上還是一個完整的HTTP協定,是以用戶端不受服務端選型影響。

Spring Webflux學習記錄

小結

從目前的初步了解來看,在一般的業務中實戰Reactive就是個高風險的選型,因為有性能壓力的往往隻會是少數幾個接口,而開發模型混合之後維護難度會加大不少,這或許就是節約成本的代價。