spring-webflux真 · 流式編程

前言
今天我們來看下webflux比較奇特的操作。當然這些操作,在我們以前的web應(yīng)用中很難實現(xiàn)的,至少實現(xiàn)起來并不容易。
webflux
下面是一個webflux流式編程的典型樣例,簡單來說就是,前端請求一次,收到請求后會源源不斷地向前端傳輸數(shù)據(jù)。
這感覺就像我們的水管一樣,前端發(fā)送一個開啟傳輸?shù)闹噶?,然后后端就開始像放水一樣像前端傳輸數(shù)據(jù)了。
接口配置
下面是接口的實現(xiàn),這里和我們前面分享的接口有所不同,這里響應(yīng)數(shù)據(jù)傳輸?shù)娜萜饔玫氖?code style="overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">Flux,之前都用的是Mono。關(guān)于兩者的區(qū)別,根據(jù)我查到的資料,Mono一般用于返回0或1個元素的異步序列Publisher,也就和我們傳統(tǒng)的MVC很像;而Flux時返回0或者N個元素組成的Publisher(Publisher的作用類似于消息分發(fā)中心這樣的角色,核心方法是subscribe)
public Mono<ServerResponse> sendTimePerSec(ServerRequest serverRequest) {
return ServerResponse.ok().contentType(MediaType.TEXT_EVENT_STREAM).body( // 1
Flux.interval(Duration.ofSeconds(1)). // 2
map(l -> new SimpleDateFormat("HH:mm:ss").format(new Date())),
String.class);
}
這個接口的作用是,間隔一秒向前端發(fā)送當前時間,時間格式HH:mm:ss。
這里需要注意的是,這里的請求頭設(shè)置的是TEXT_EVENT_STREAM,這種響應(yīng)頭和之前的區(qū)別后面我們會演示,這里大家留意下就行。
interval是一個延遲回調(diào)方法,當達到指定的延遲時間period,方法返回值是循環(huán)的次數(shù),從0開始。下面是官方文檔給出的interval方法的示意圖,還是比較形象的

map方法應(yīng)該不需要過多說明吧,用過lambda表達式的小伙伴,應(yīng)該很熟悉,在這里它的作用也是做類型轉(zhuǎn)換,這里的寫法是lambda的寫法,完整寫法是這樣的:
.map(new Function<Long, String>() {
@Override
public String apply(Long l) {
System.out.println(l);
return new SimpleDateFormat("HH:mm:ss").format(new Date());
}
})
路由配置
這里是設(shè)置我們接口的路由信息,包括請求方式、地址、處理器等。
@Bean
public RouterFunction<ServerResponse> routSayHi(HiHandler handler) {
return RouterFunctions
.route(RequestPredicates.GET("webflux/hi")
.and(RequestPredicates.accept(MediaType.ALL)), handler::sayHi)
.andRoute(RequestPredicates.GET("/webflux/test")
.and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), handler::sendTimePerSec);
}
測試運行
訪問我們的接口,你會發(fā)現(xiàn),雖然前端只訪問了一次,數(shù)據(jù)一直在持續(xù)輸出,當然如果你點擊瀏覽器上面的X,就相當于本次請求結(jié)束,數(shù)據(jù)也就不再傳輸了。

而且后端也在持續(xù)輸出,就像水管一樣,水一直在流動。

另外根據(jù)我們運行結(jié)果來看,我們發(fā)現(xiàn)Flux類型的響應(yīng)數(shù)據(jù)和之前的響應(yīng)類型(Mono)相比是不一樣的,在Flux的響應(yīng)數(shù)據(jù)中,我們會發(fā)現(xiàn)接口的響應(yīng)頭變成了text/event-stream;charset=UTF-8,也就是我們前面執(zhí)行的響應(yīng)頭,而且多了一個請求頭transfer-encoding: chunked,這個請求頭表明我們的響應(yīng)數(shù)據(jù)長度是不確定的,據(jù)說這種方式占用的資源更少,可以讓數(shù)據(jù)一塊一塊輸出。


關(guān)于兩者區(qū)別,我暫時還不清楚,但是如果把Mono的響應(yīng)體改成TEXT_EVENT_STREAM,返回結(jié)果依然正常,但是如果把Flux的響應(yīng)類型改成APPLICATION_JSON,雖然頁面依然會持續(xù)有數(shù)據(jù)輸出,但是看起來比剛才奇怪了:

好了, 關(guān)于webflux的流式編程就先演示到這里,后面我們看情況繼續(xù)深入。
總結(jié)
經(jīng)過這幾天的摸索和學(xué)習,我慢慢對webflux有了更深入的了解和認識,但是同時也發(fā)現(xiàn)webflux的相關(guān)只是真的是太多了,而且是越學(xué)習感覺不知道的東西越多(知識的悖論,知識的詛咒),所以也只能說自己以前想的太天真了,竟然還想通過三五天的時間就學(xué)會webflux。
另外,我也發(fā)現(xiàn)目前市面上關(guān)于webflux的相關(guān)知識、書籍和博客都比較少,而且大部分都不夠系統(tǒng),所以關(guān)于webflux的學(xué)習,我已經(jīng)做好了持久戰(zhàn)的準備,以后應(yīng)該不會每天更新,這樣確實比較容易上頭,另一方面,如果積累的知識點不夠的話,很容易言之無物,所以綜合起來看,拉長戰(zhàn)線會是一個比較合理的選擇。
最后,給各位i小伙伴一個建議,在學(xué)習的時候,一定要定期去復(fù)習,不然之前學(xué)習的知識點比較容易忘記。這兩天我在用線程池的時候,還專門去翻了之前的博客,因為好多東西又忘了
- END -