Spring WebFlux和Reactive程式設計
在看到Jurgen Hoeller引入新的Spring 5功能後,我終於開始嘗試在尚未釋出的Spring Boot 2.0.0 Snapshot中嘗試新的Spring WebFlux專案。開始吧:
Maven WebFlux專案生成
- 轉到Spring啟動應用程式生成器
- 在Spring Boot版本中選擇“2.0.0”以上版本
- 在依賴項中搜尋“ Reactive Web ”
- 儲存生成的maven專案
演示(反應端點)
在剛剛生成的Spring WebFlux專案中,讓我們構建一個REST端點,以Reactive方式獲取通用儲存Item 。首先讓我們開始:
@RestController public class ItemsReactiveController { @Autowired private IItemsService iItemsService; public Flux<Item> findById(String id) { try { System.out.println("Getting the data from DB..."); Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } return Flux.just(iItemsService.findById(id)); } @GetMapping(value = "/store/{id}", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public void getItemById(@PathVariable String id) { System.out.println("Controller start...."); findById(id).subscribe(new Subscriber<Item>() { @Override public void onSubscribe(Subscription s) { s.request(Long.MAX_VALUE); } @Override public void onNext(Item t) { System.out.println("Retrieved: "+t.getName()); } @Override public void onError(Throwable t) { } @Override public void onComplete() { } }); System.out.println("End of method."); } } |
程式碼詳細
方法findById返回Flux <Item>型別。Flux是一種以反應方式返回0..N項的資料型別。另外一種可能性是使用返回0或1項的Mono<Item>型別。
Jurgen Hoeller清楚地提到如果端點返回上述資料型別之一,那麼實際上沒有返回結果,但是Spring給呼叫者一個管道,其中結果將最終落地。正如您從NodeJS所知,但是在Spring方式中,引擎蓋下的機制非常接近EventLoop。要從Reactive端點獲取任何資料,您需要訂閱返回的管道。如果您想獲得結果的回撥,那麼管道上的訂閱階段或錯誤就會被使用來自反應流的訂閱者,這是Project reactor的底層實現。
第一次測試:
專案原始碼:https://bitbucket.org/tomask79/spring-reactive-rest.git
讓我們用Oracle Store中現有Item的{id}呼叫先前建立的端點(我不會厭煩使用的JPA配置,它不是演示的主題)。點選瀏覽器:http://localhost:8081/store/{id}
系統輸出:
Controller start.... Getting the data from DB... <p class="indent">[EL Fine]: sql: 2017-03-20 14:19:56.321--ServerSession(26882836)--Connection(29914401)--SELECT ID, ITEM_NAME FROM ITEMS WHERE (ID = ?) bind => [1 parameter bound] Retrieved: <Name of the Item> End of method. |
如您所見,程式碼輸出每個步驟:
- 呼叫控制器(Controller start ....)
- 獲取item呼叫服務(Retrieved: <Name of the Item>)
- 達到了方法的結束。(End of method)
預設情況下,從主執行緒獲取訂閱資料,當然因為我使用的資料儲存不提供反應式驅動程式(Oracle),因此呼叫儲存是阻塞的。目前,支援反應式程式設計(解鎖通話)的儲存是:
- Mongo
- Redis
- Cassandra
- Postgres
當然,啟用檢查新專案Spring Data“Kay”,才能在上面提到的Spring Data專案中啟用反應範例。
要實際啟用非同步釋出我們的專案,我們需要將控制器更改為:
package com.example.controller; import com.example.domain.Item; import com.example.service.IItemsService; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; import reactor.core.scheduler.Scheduler; import reactor.core.scheduler.Schedulers; /** * Created by Tomas.Kloucek on 17.3.2017. */ @RestController public class ItemsReactiveController { @Autowired private IItemsService iItemsService; public Flux<Item> findById(String id) { try { System.out.println("Getting the data from DB..."); Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } return Flux.just(iItemsService.findById(id)).publishOn(Schedulers.parallel()); } @GetMapping(value = "/store/{id}", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public void getItemById(@PathVariable String id) { System.out.println("Controller start...."); findById(id).subscribe(v -> { System.out.println("Consumed: "+v.getId()); }); System.out.println("End of method."); } } |
- 改變了訂閱只獲得結果。如果你知道RxJava你應該熟悉。
- 新增了publishOn方法,並排程了用於非同步釋出Item的執行緒。
現在,如果我們從瀏覽器再次點選端點,輸出將是:
Controller start.... Getting the data from DB... <p class="indent">[EL Fine]: sql: 2017-03-20 14:16:49.69--ServerSession(18245293)--Connection(9048111)--SELECT ID, ITEM_NAME FROM ITEMS WHERE (ID = ?) bind => [1 parameter bound] End of method. Consumed: <ID of your Item entity> |
正如您所看到的,Spring在給出訂閱請求資料之前就已到達方法的最後(End of method)。
如何建立客戶端以呼叫Reactive Endpoint
讓我們用程式碼建立另一個Spring Boot WebReactive應用程式:
@SpringBootApplication public class DemoApplication { @Bean public WebClient webClient() { return WebClient.create("http://<reactiveAppHost>:<reactiveAppPort>"); } @Bean CommandLineRunner launch(WebClient webClient) { return args -> { webClient.get().Yuri("/store/{id}") .accept(MediaType.TEXT_EVENT_STREAM) .exchange() .flatMap(cr -> cr.bodyToFlux(Item.class)) .subscribe(v -> { System.out.println("Received from MS: "+v.getName()); }); }; } public static void main(String args[]) { new SpringApplicationBuilder(DemoApplication.class).properties (Collections.singletonMap("server.port", "8082")) .run(args); } } |
程式碼詳細:
要呼叫Reactive端點,您需要首先獲取WebClient例項。在演示案例中放入create方法http://localhost:8081.。由WebClient.exchange()方法呼叫執行的自呼叫方法,
但要實際在管道上放置訂閱以獲取結果,您需要呼叫ClientRequest.bodyToFlux(<ResultClassMapping> .class),這種訂閱才是可能的。如果我們執行這個應用程式,那麼結果應該是:
Started DemoApplication in 4.631 seconds (JVM running for 4.954) Received from MS: <Item text> |
這部分客戶端程式碼:
git clone https://tomask79@bitbucket.org/tomask79/spring-reactive-rest-client.git
用於非同步呼叫REST端點的API是否必要?
我對這種訂閱和非同步呼叫端點的新反應趨勢的觀點是一種悲觀。如果我需要非同步呼叫端點,那麼JMS或AMPQ是第一個讓我進入大腦的想法,特別是在MicroServices中。但我們會看到這將如何發展。Spring Framework 5中的其他計劃更改很有希望:
- 帶有Angular的函式框架,類似Router
- 支援Project Jigsaw
- 註冊beanLamdas。
本站文章點選標題看原文!
相關文章
- 實戰Spring Boot 2.0 Reactive程式設計系列 - WebFlux初體驗Spring BootReact程式設計WebUX
- Reactive Spring實戰 -- WebFlux使用教程ReactSpringWebUX
- Java程式設計方法論-Spring WebFlux篇 01 為什麼需要Spring WebFlux 上Java程式設計SpringWebUX
- Java程式設計方法論-Spring WebFlux篇 01 為什麼需要Spring WebFlux 下Java程式設計SpringWebUX
- Spring Boot 中的響應式程式設計和 WebFlux 入門Spring Boot程式設計WebUX
- Spring webflux 函數語言程式設計web框架SpringWebUX函數程式設計框架
- Spring Boot 2 (十):Spring Boot 中的響應式程式設計和 WebFlux 入門Spring Boot程式設計WebUX
- Spring Cloud Stream的函式式和響應式Reactive程式設計特點 - spring.ioSpringCloud函式React程式設計
- Spring WebFlux 的設計及工作原理剖析SpringWebUX
- Spring響應式Reactive程式設計的10個陷阱 -Jeroen RosenbergSpringReact程式設計ROS
- Java程式設計方法論-Spring WebFlux篇 Reactor-Netty下HttpServer 的封裝Java程式設計SpringWebUXReactNettyHTTPServer封裝
- Reactive Spring實戰 -- 理解Reactor的設計與實現ReactSpring
- ThreadLocal難以在非同步程式設計或Reactive程式設計中使用 - bsideupthread非同步程式設計ReactIDE
- Java程式設計方法論-Spring WebFlux篇 Reactor-Netty下TcpServer的功能實現 1Java程式設計SpringWebUXReactNettyTCPServer
- 深入剖析 Spring WebFluxSpringWebUX
- WebFlux 和 Spring Security 會碰出哪些火花?WebUXSpring
- Spring WebFlux安全配置教程和原始碼 - vinsguruSpringWebUX原始碼
- 該不該使用Reactive程式設計?先預覽一下Loom專案中的Reactive模型和協程 - frankelReact程式設計OOM模型
- Spring Webflux國際化SpringWebUX
- Spring Webflux與事務SpringWebUX
- Spring WebFlux之HttpHandler的探索SpringWebUXHTTP
- Day67 Spring AOP(面向切面程式設計) 和代理設計模式Spring程式設計設計模式
- Spring 學習筆記(6)Spring和資料庫程式設計Spring筆記資料庫程式設計
- ref和reactiveReact
- Spring WebFlux效能真的超過Spring Servlet ? - GavinSpringWebUXServlet
- 使用Java和Spring WebFlux構建響應式微服務JavaSpringWebUX微服務
- Spring之切面程式設計Spring程式設計
- 第七章:C#響應式程式設計System.ReactiveC#程式設計React
- Spring boot webflux 中實現 RequestContextHolderSpring BootWebUXContext
- Spring WebFlux的明顯陷阱 - ŁukaszKyćSpringWebUX
- Spring WebFlux 基礎教程:WebSocket 使用SpringWebUX
- Spring AOP——Spring 中面向切面程式設計Spring程式設計
- Spring系列:基於Spring-AOP和Spring-Aspects實現AOP切面程式設計Spring程式設計
- Java9第四篇-Reactive Stream API響應式程式設計JavaReactAPI程式設計
- 《響應式程式設計(Reactive Programming)介紹》文章總結與案例分析程式設計React
- Spring 面向方面程式設計 AOPSpring程式設計
- Spring生態系統中的Reactor、WebFlux和RSocket區別? - FranciscoSpringReactWebUX
- Java 網路程式設計(TCP程式設計 和 UDP程式設計)Java程式設計TCPUDP