各位Javaer們,你們都在用SpringMVC吧?當咱們不亦樂乎的用着SpringMVC框架的時候,Spring5.x又悄(da)無(zhang)聲(qi)息(gu)的推出了Spring WebFlux。web? 不是已經有SpringMVC這麼好用的東西了麼,爲啥又冒出個WebFlux? 這玩意兒是什麼鬼?html
SpringMVC是同步阻塞的IO模型,資源浪費相對來講比較嚴重,當咱們在處理一個比較耗時的任務時,例如:上傳一個比較大的文件,首先,服務器的線程一直在等待接收文件,在這期間它就像個傻子同樣等在那兒(放學別走),什麼都幹不了,好不容易等到文件來了而且接收完畢,咱們又要將文件寫入磁盤,在這寫入的過程當中,這根線程又再次懵bi了,又要等到文件寫完才能去幹其它的事情。這一前一後的等待,不浪費資源麼?java
沒錯,Spring WebFlux就是來解決這問題的,Spring WebFlux能夠作到異步非阻塞。仍是上面那上傳文件的例子,Spring WebFlux是這樣作的:線程發現文件還沒準備好,就先去作其它事情,當文件準備好以後,通知這根線程來處理,當接收完畢寫入磁盤的時候(根據具體狀況選擇是否作異步非阻塞),寫入完畢後通知這根線程再來處理(異步非阻塞狀況下)。這個用腳趾頭都能看出相對SpringMVC而言,能夠節省系統資源。666啊,有木有!react
若是你以爲java8的lambda寫起來很爽,那麼,你會再次喜歡上Spring WebFlux,由於它支持函數式編程,得益於對於reactive-stream的支持(經過reactor框架來實現的),喜歡java8 stream的又有福了。爲何要函數式編程? 這個別問我,我也不知道,或許是由於bi格高吧,哈哈,開玩笑啦。web
之前,咱們的應用都運行於Servlet容器之中,例如咱們你們最爲熟悉的Tomcat, Jetty...等等。而如今Spring WebFlux不只能運行於傳統的Servlet容器中(前提是容器要支持Servlet3.1,由於非阻塞IO是使用了Servlet3.1的特性),還能運行在支持NIO的Netty和Undertow中。spring
因此,看完Spring WebFlux的新特性以後,心裏五味雜陳的我,只能用一個表情來形容:shell
可是學習仍是要學的,畢竟Spring推出的......編程
Spring WebFlux是隨Spring 5推出的響應式Web框架。創建在異步非阻塞的IO框架之上的一個新的,其基本的架構以下:
api
Spring提供了完整的支持響應式的服務端技術棧。spring-mvc
如上圖所示,左側爲基於spring-webmvc的技術棧,右側爲基於spring-webflux的技術棧,能夠看到SpringMVC技術棧給予Serverlet容器,如Tomcat容器,SpringWebFlux基於HTTP/Reactive Stream.服務器
依賴於SpringBoot的強大,咱們只要在配置文件添加依賴便可。
Gradle 依賴
compile('org.springframework.boot:spring-boot-starter-webflux')
或者Maven構建的依賴於
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency>
Spring團隊在開發WebFlux上儘可能和SpringMVC靠攏,所以咱們能夠直接使用一個簡單的SpringMVC項目有改形成Spring WebFlux的項目,具體以下。
下面是咱們再熟悉不過的接口應用,訪問http://localhost:8080/mono 便可看到返回了一個字符串
@GetMapping("/mono") public String baseApi() { return "Hello,Reactive Program"; }
改造後的內容以下:
@GetMapping("/mono") public Mono<String> baseApi() { //1 return Mono.just("Hello,Reactive Program"); //2 }
主要有兩處改造
1 返回的再也不是簡單的對象,而是使用的是Mono封裝的單個文檔信息(返回集合使用Flux
2 返回的時候咱們須要構造一個Mono類型的數據,所以使用Mono.just(T t) 構造
能夠看大,執行的結果以下:
$ curl -X GET http://localhost:8080/mono Hello,Reactive Program
效果和SpringMVC 並沒有區別,一樣的咱們返回集合列表查看效果
@GetMapping("/flux") public Flux<String> getFluxString() { String[] dataSet = new String[]{"This is 1", "This is 2", "This is 3", "This is 4"}; return Flux.fromIterable(Arrays.asList(dataSet)); }
結果也和預期一致,那麼不只要思考了,一樣和SpringMVC達到一致的效果,爲何咱們要用WebFlux?
首先看着這二者並沒有區別,其實實際上和文章首頁的架構圖示同樣,其底層核心的變了,實現接口,並再是基於Servlet,而是基於Http/Reactive Stream ,咱們在接口方法添加參數
@GetMapping("/flux") public Flux<String> getFluxString(HttpServerRequest request) {....}
此時訪問flux接口,會報錯
java.lang.IllegalStateException: No primary or default constructor found for interface org.springframework.http.HttpRequest
意思是非法的狀態異常,沒有org.springframework.http.HttpRequest的構造參數被發現,這說明WebFlux的實現已經再也不是Serverlet了
下面我是實現SSE(服務器推送),注意這裏和Socket有所區別,Socket是雙向通訊,這是單向通訊,由服務器向客戶端推送消息
@GetMapping(value = "/sse/object", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<Book> sseBook() { return Flux.interval(Duration.ofSeconds(1)) .map( second -> new Book() .setId(Stirng.valueOf(second)) .setName("深刻淺出Flux響應式Web編程" + second) .setPrice("12") ).take(5); }
模型Book須要lombok支持,沒有的話,請手動完成set、get方法,並在Set方法尾部return this
@Data @Accessors(chain = true) public class Book { private String id; private String name; private String price; private Date createTime = new Date(); }
首先說明一下produces = MediaType.TEXT_EVENT_STREAM_VALUE
表示這是一個事件流,返回的是Flux類型,推送的間隔爲1s,最後take(times)表示推送的次數,沒有take表示無限流,times表示推送的次數,咱們在shell中嘗試調用下,看看效果
$ curl -X GET http://localhost:8080/sse/object data:{"id":"0","name":"Flux響應式Web編程0","price":"12","createTime":"2018-09-09T12:46:10.445+0000"} data:{"id":"1","name":"Flux響應式Web編程1","price":"12","createTime":"2018-09-09T12:46:11.444+0000"} data:{"id":"2","name":"Flux響應式Web編程2","price":"12","createTime":"2018-09-09T12:46:12.444+0000"} data:{"id":"3","name":"Flux響應式Web編程3","price":"12","createTime":"2018-09-09T12:46:13.445+0000"} data:{"id":"4","name":"Flux響應式Web編程4","price":"12","createTime":"2018-09-09T12:46:14.444+0000"}
須要注意的是,在建立時間上,是每一個1s鍾由服務器推送過來的,這是和SpringMVC有着巨大的區別
.
Spring團隊在實現WebFlux的有了另外的實現方式,利用RouterFuntion & HandleFunction,這裏不作過多的贅述,這種方式的效果和上述效果一致,能夠對比學習,代碼以下:
向Spring容器中注入RouterFunction
Bean對象
@Configuration public class RouteConfig { @Bean public RouterFunction<ServerResponse> timeRoute(){ return route(GET("/time"),TimeHandle::getTime) .andRoute(GET("/sse"),TimeHandle::sendTimeWithSSE); } }
具體邏輯實現
public class TimeHandle { private static SimpleDateFormat simpleDateFormat =new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); public static Mono<ServerResponse> getTime(ServerRequest serverRequest){ return ok().contentType(MediaType.APPLICATION_JSON_UTF8).body(Mono.just(simpleDateFormat.format(new Date())),String.class); } // 實現時間SSE推送注意MediaType類型 public static Mono<ServerResponse> sendTimeWithSSE(ServerRequest serverRequest){ return ok().contentType(MediaType.TEXT_EVENT_STREAM).body( Flux.interval(Duration.ofSeconds(1)).map(value -> simpleDateFormat.format(new Date())) ,String.class); } }
總體來講仍是比較簡單的,請繼續關注後期的WebFlux的學習過程~