javascript
Spring Reactor教程
在RESTful服務(wù)的世界中,實際上實際上是在幕后進行許多工作,我們通常必須在應(yīng)用程序中進行很多處理,而實際上并不會影響需要發(fā)送給真實用戶的響應(yīng)。 可以被動地做出這些業(yè)務(wù)決策,以便它們對與應(yīng)用程序交互的用戶沒有任何影響。 Spring Framework為我們提供了一個出色的項目,稱為Spring Reactor項目,它使我們能夠在后臺很好地管理此后臺處理。 在學(xué)習(xí)本課程之前,我們必須注意一件事,即反應(yīng)式編程與并發(fā)編程并不相同 。
RESTful編程中用于響應(yīng)行為的用例之一是,在大多數(shù)情況下,服務(wù)從根本上是阻塞和同步的。 響應(yīng)式編程使我們可以擴展到同步線程的范圍之外,并且可以在不表現(xiàn)阻塞行為的情況下完成復(fù)雜的編排。 讓我們深入學(xué)習(xí)本課程,以了解如何將這種反應(yīng)性行為集成到基于Spring Boot的應(yīng)用程序中。
目錄
1.簡介 2. JVM中的Reactor 3.使用Maven制作Spring Boot項目 4.添加Maven依賴項 5.項目結(jié)構(gòu) 6.了解示例應(yīng)用程序 7.定義POJO模型 8.定義服務(wù) 9.定義事件使用者 10.定義Java配置 11.定義Spring Boot類 12.運行項目 13.結(jié)論 14.下載源代碼1.簡介
在本Spring Reactor課程中,我們將學(xué)習(xí)如何在Spring Boot項目中開始反應(yīng)性行為,以及如何在同一應(yīng)用程序本身中開始產(chǎn)生和使用消息。 除了一個簡單的項目外,當(dāng)有多個不同類型的請求處理程序時,我們還將看到Spring Reactive流如何工作以及如何管理請求。
隨著的起義微服務(wù) ,涉及的服務(wù)之間的異步通信的必要性成為主流需求。 為了在涉及的各種服務(wù)之間進行通信,我們可以使用Apache Kafka之類的項目。 現(xiàn)在,異步通信對于同一應(yīng)用程序中的耗時請求也很理想。 這是Spring Reactor的實際用例發(fā)揮作用的地方。
請注意,僅當(dāng)用戶不希望直接從應(yīng)用程序獲得響應(yīng)時,才使用此應(yīng)用程序中演示的Reactor模式,因為我們僅使用此Reactor演示執(zhí)行后臺作業(yè)。 當(dāng)開發(fā)人員可以為應(yīng)用程序分配更多的堆內(nèi)存(取決于該應(yīng)用程序?qū)⑹褂玫木€程數(shù))并且他們想并行執(zhí)行任務(wù)并且任務(wù)的執(zhí)行順序不合理時,使用Reactor是一個很好的選擇。沒關(guān)系。 這一點實際上很重要,因此我們將通過重新措辭再說一遍,當(dāng)并行執(zhí)行作業(yè)時,無法確認(rèn)作業(yè)的執(zhí)行順序 。
2. JVM中的Reactor
正如Spring本身所言,Reactor是JVM上異步應(yīng)用程序的基礎(chǔ)框架,它在適度的硬件上,可以用最快的非阻塞Dispatcher 每秒處理超過15,000,000個事件 。 聽起來,Reactor框架基于Reactor設(shè)計模式 。
關(guān)于Spring Reactor,最重要的是該框架為使用Spring開發(fā)應(yīng)用程序的Java開發(fā)人員提供的抽象級別。 這種抽象使得在我們自己的應(yīng)用程序中實現(xiàn)功能非常容易。 讓我們從一個示例項目開始,看看如何在接近現(xiàn)實的應(yīng)用程序中使用該框架。 該反應(yīng)堆項目還支持與反應(yīng)堆IPC組件進行無阻塞的進程間通信(IPC),但其討論不在本課程的討論范圍之內(nèi)。
3.使用Maven制作Spring Boot項目
我們將使用許多Maven原型之一為我們的示例創(chuàng)建一個示例項目。 要創(chuàng)建項目,請在將用作工作空間的目錄中執(zhí)行以下命令:
創(chuàng)建一個項目
mvn archetype:generate -DgroupId=com.javacodegeeks.example -DartifactId=JCG-BootReactor-Example -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false如果您是第一次運行maven,則完成生成命令將花費幾秒鐘,因為maven必須下載所有必需的插件和工件才能完成生成任務(wù)。 運行該項目后,我們將看到以下輸出并創(chuàng)建該項目:
Spring Reactor項目設(shè)置
4.添加Maven依賴項
創(chuàng)建項目后,請隨時在您喜歡的IDE中打開它。 下一步是向項目添加適當(dāng)?shù)腗aven依賴關(guān)系。 我們將在項目中使用以下依賴項:
- spring-boot-starter-web :此依賴關(guān)系將該項目標(biāo)記為Web項目,并添加了依賴關(guān)系以創(chuàng)建控制器并創(chuàng)建與Web相關(guān)的類
- reactor-bus :這是將所有與Reactor相關(guān)的依賴項引入項目類路徑的依賴項
- spring-boot-starter-test :此依賴項將所有與測試相關(guān)的JAR收集到項目中,例如JUnit和Mockito
這是pom.xml文件,其中添加了適當(dāng)?shù)囊蕾図?#xff1a;
pom.xml
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>1.5.10.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>io.projectreactor</groupId><artifactId>reactor-bus</artifactId><version>2.0.8.RELEASE</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>在Maven Central上找到最新的Maven依賴項。 我們還為Spring引導(dǎo)項目添加了一個Maven插件,該插件可以幫助我們將該項目變成可運行的JAR,以便無需任何現(xiàn)代工具和依賴項即可輕松部署該項目。 我們從此插件獲得的JAR已完全準(zhǔn)備好作為可執(zhí)行文件進行部署。
最后,要了解添加此依賴項時添加到項目中的所有JAR,我們可以運行一個簡單的Maven命令,當(dāng)我們向其添加一些依賴項時,該命令使我們能夠查看項目的完整依賴關(guān)系樹。 當(dāng)我們以適當(dāng)?shù)膶哟谓Y(jié)構(gòu)方式添加一些自己的依賴項時,此依賴關(guān)系樹還將顯示添加了多少個依賴項。 這是我們可以使用的相同命令:
檢查依賴樹
mvn dependency:tree當(dāng)我們運行此命令時,它將向我們顯示以下依賴關(guān)系樹:
Maven依賴樹
注意到了什么? 只需在項目中添加三個依賴項,即可添加如此多的依賴項。 Spring Boot本身會收集所有相關(guān)的依賴項,因此在此方面不做任何事情。 最大的優(yōu)點是,由于Spring Boot項目的pom文件本身可以管理和提供這些依賴關(guān)系,因此可以確保所有這些依賴關(guān)系相互兼容。
5.項目結(jié)構(gòu)
在繼續(xù)進行并開始為該項目編寫代碼之前,讓我們介紹一下一旦完成將所有代碼添加到項目中之后將擁有的項目結(jié)構(gòu),以便我們知道將在該項目中放置類的位置:
Spring Reactor項目結(jié)構(gòu)
我們將項目分為多個包,以便遵循關(guān)注點分離的原則,并且代碼保持模塊化,這使得項目的擴展相當(dāng)容易。
6.了解示例應(yīng)用程序
為了使應(yīng)用程序易于理解并且接近實際情況,我們將考慮一種物流應(yīng)用程序的場景,該應(yīng)用程序管理放置在系統(tǒng)中的各種貨物的交付。
該應(yīng)用程序從外部供應(yīng)商處接收有關(guān)在給定地址處交付給客戶的貨件位置的更新。 我們的應(yīng)用程序收到此更新后,便會執(zhí)行各種操作,例如:
- 在數(shù)據(jù)庫中更新裝運位置
- 向用戶的移動設(shè)備發(fā)送通知
- 發(fā)送電子郵件通知
- 發(fā)送短信給用戶
我們選擇對這些操作表現(xiàn)出反應(yīng)性行為,因為用戶不依賴于這些操作是否實時準(zhǔn)確地進行,因為它們主要是后臺任務(wù),這也可能會花費一些時間,并且如果裝運狀態(tài)更新晚了幾分鐘。 讓我們首先開始創(chuàng)建模型。
7.定義POJO模型
我們將從定義我們的POJO開始,該POJO表示要發(fā)送給客戶的shipmentId ,該shipmentId具有currentLocation , currentLocation等字段。讓我們在這里定義此POJO:
Shipment.java
package com.javacodegeeks.example.model;public class Shipment {private String shipmentId;private String name;private String currentLocation;private String deliveryAddress;private String status;//standard setters and getters }我們在這里定義了一些基本字段。 為了簡潔起見,我們省略了標(biāo)準(zhǔn)的getter和setter方法,但是由于Jackson在對象的序列化和反序列化過程中使用它們,因此必須將它們制成。
8.定義服務(wù)
我們將定義一個基本接口,該接口定義我們接下來將要使用的功能的合同,該接口將定義一旦應(yīng)用程序消耗了偶數(shù)就需要執(zhí)行的業(yè)務(wù)邏輯。
這是我們將使用的合同定義:
ShipmentService.java
package com.javacodegeeks.example.service;import com.javacodegeeks.example.model.Shipment;public interface ShipmentService {void shipmentLocationUpdate(Shipment shipment); }在此接口中,我們只有一個方法定義,因為這是我們現(xiàn)在所需要的。 現(xiàn)在讓我們繼續(xù)實現(xiàn)此服務(wù),在這里我們將實際演示一個sleep方法,該方法只是模擬此類的操作行為:
ShipmentServiceImpl.java
package com.javacodegeeks.example.service;import com.javacodegeeks.example.model.Shipment; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service;@Service public class ShipmentServiceImpl implements ShipmentService {private final Logger LOG = LoggerFactory.getLogger("ShipmentService");@Overridepublic void shipmentLocationUpdate(Shipment shipment) throws InterruptedException {LOG.info("Shipment data: {}", shipment.getShipmentId());Thread.sleep(3000);LOG.info("Shipment with ID: {} reached at javacodegeeks!!!", shipment.getShipmentId());} }出于說明目的,在調(diào)用此服務(wù)并附帶裝運詳細信息時,它僅提供一些打印語句,使用3000毫秒的延遲來實現(xiàn)我們在上一節(jié)中定義的操作可能要花費的時間。 請注意,這些操作中的每一個可能花費的時間遠遠超過3秒,但是應(yīng)用程序沒有時間(直到線程開始堆積在需要管理的應(yīng)用程序的堆內(nèi)存上)。
9.定義事件使用者
在本節(jié)中,我們最終將看到如何定義一個偵聽事件發(fā)運位置更新的使用者。 可以通過將事件進行裝運更新來調(diào)用此使用者,該事件將在我們即將定義和使用的SPring的EventBus上放置。
EventHandler.java
package com.javacodegeeks.example.handler;import com.javacodegeeks.example.model.Shipment; import com.javacodegeeks.example.service.ShipmentService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import reactor.bus.Event; import reactor.fn.Consumer;@Service public class EventHandler implements Consumer<Event<Shipment>> {private final ShipmentService shipmentService;@Autowiredpublic EventHandler(ShipmentService shipmentService) {this.shipmentService = shipmentService;}@Overridepublic void accept(Event<Shipment> shipmentEvent) {Shipment shipment = shipmentEvent.getData();try {shipmentService.shipmentLocationUpdate(shipment);} catch (InterruptedException e) {//do something as bad things have happened}} }此使用者服務(wù)在事件總線中接受該對象,并通知我們的服務(wù)類,以便它可以異步執(zhí)行必要的操作。 請注意,我們還將定義一個線程池,該線程池將用于運行此使用者,以便可以使用不同的線程來運行服務(wù)方法調(diào)用。 即使我們自己沒有定義線程池,Spring Boot也會使用固定數(shù)量的最大線程池為我們完成此任務(wù)。
此消費者類的好處是,它從事件總線接收到了Shipment對象本身,并且無需在類本身中進行轉(zhuǎn)換或強制轉(zhuǎn)換,這是常見的錯誤區(qū)域,并且還增加了業(yè)務(wù)邏輯所需的時間執(zhí)行。
10.定義Java配置
我們可以在應(yīng)用程序中使用Java定義配置。 讓我們在這里做這些定義:
ReactorConfig.java
package com.javacodegeeks.example.config;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import reactor.Environment; import reactor.bus.EventBus;@Configuration public class ReactorConfig {@BeanEnvironment env() {return Environment.initializeIfEmpty().assignErrorJournal();}@BeanEventBus createEventBus(Environment env) {return EventBus.create(env, Environment.THREAD_POOL);} }顯然,這里沒有什么特別的。 我們只是用一些數(shù)字(這里是默認(rèn)值)初始化了線程池。 我們只是想演示如何根據(jù)您的應(yīng)用程序用例來更改線程數(shù)。
11.定義Spring Boot類
在最后階段,我們將創(chuàng)建Spring Boot類,通過該類我們可以發(fā)布一條消息,該消息可以由我們先前定義的事件處理程序使用。 這是主類的類定義:
應(yīng)用程序
package com.javacodegeeks.example;import com.javacodegeeks.example.handler.EventHandler; import com.javacodegeeks.example.model.Shipment; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import reactor.bus.Event; import reactor.bus.EventBus;import static reactor.bus.selector.Selectors.$;@SpringBootApplication public class Application implements CommandLineRunner {private final Logger LOG = LoggerFactory.getLogger("Application");private final EventBus eventBus;private final EventHandler eventHandler;@Autowiredpublic Application(EventBus eventBus, EventHandler eventHandler) {this.eventBus = eventBus;this.eventHandler = eventHandler;}public static void main(String[] args) {SpringApplication.run(Application.class, args);}@Overridepublic void run(String... strings) throws Exception {eventBus.on($("eventHandler"), eventHandler);//Publish messages herefor (int i = 0; i < 10; i++) {Shipment shipment = new Shipment();shipment.setShipmentId(String.valueOf(i));eventBus.notify("eventHandler", Event.wrap(shipment));LOG.info("Published shipment number {}.", i);}} }我們使用了CommandLineRunner接口來使此類運行代碼,從而可以測試所編寫的生產(chǎn)者和配置類代碼。 在此類中,我們將消息發(fā)布到指定的主題,并在我們在同一應(yīng)用程序中定義的使用者類中偵聽該消息。 請注意,我們使用Spring自己的事件總線來承載作業(yè),并且這些作業(yè)不會放在磁盤上。 如果使用Spring Boot執(zhí)行器正常終止了該應(yīng)用程序,則這些作業(yè)將自動保留在磁盤上,以便在應(yīng)用程序重新啟動時可以重新排隊。
在下一節(jié)中,我們將使用簡單的Maven命令運行項目。
12.運行項目
既然完成了主類定義,我們就可以運行我們的項目。 使用maven可以輕松運行應(yīng)用程序,只需使用以下命令:
pom.xml
mvn spring-boot:run一旦執(zhí)行了以上命令,我們將看到一條消息已經(jīng)發(fā)布,并且同一應(yīng)用在事件處理程序中使用了該消息:
運行Spring Reactor應(yīng)用程序
我們看到使用非阻塞模式下使用的CommandLineRunner方法啟動應(yīng)用程序時,事件已發(fā)布。 事件發(fā)布后,事件處理程序?qū)⒉⑿惺褂盟?如果仔細研究使用者,您會注意到Spring在線程池中定義了四個線程來管理這些事件。 這是Spring定義的用于并行管理事件的線程數(shù)的默認(rèn)限制。
13.結(jié)論
在本課程中,我們研究了構(gòu)建集成了Reactor項目的Spring Boot應(yīng)用是多么容易和快捷。 就像我們已經(jīng)說過的那樣,在您的應(yīng)用程序中設(shè)計良好的反應(yīng)堆模式可以具有每秒高達15,000,000(即六個零 )事件的吞吐量。 這表明該反應(yīng)堆內(nèi)部隊列的執(zhí)行效率如何。
在我們定義的小型應(yīng)用程序中,我們演示了一種定義線程池執(zhí)行程序的簡單方法,該執(zhí)行程序定義了四個線程,而使用者使用該線程池來并行管理事件。 在依賴異步行為執(zhí)行操作的應(yīng)用程序中面臨的最常見問題之一是,當(dāng)有多個線程開始占用堆空間并在開始處理時創(chuàng)建對象時,它們很快就會耗盡內(nèi)存。 確保啟動應(yīng)用程序時,我們?yōu)閼?yīng)用程序分配良好的堆大小非常重要,這直接取決于為應(yīng)用程序定義的線程池的大小。
反應(yīng)式編程是最常見的編程風(fēng)格之一,由于應(yīng)用程序開始通過并行執(zhí)行來利用CPU內(nèi)核,因此這是一種正在興起的編程風(fēng)格,這是在應(yīng)用程序級別使用硬件的好主意。 Reactor為JVM提供了完整的非阻塞編程基礎(chǔ),并且也可用于Groovy或Kotlin。 由于Java本身不是反應(yīng)性語言,因此它本身不支持協(xié)程。 有多種JVM語言(例如Scala和Clojure)就本機性而言更好地支持反應(yīng)模型,但是Java本身并沒有做到這一點(至少直到版本9才如此)。
14.下載源代碼
這是帶有Spring Boot和Reactor模式的Java編程語言的示例。
下載您可以在此處下載此示例的完整源代碼: Reactor示例
翻譯自: https://www.javacodegeeks.com/2018/06/spring-reactor-tutorial.html
總結(jié)
以上是生活随笔為你收集整理的Spring Reactor教程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 海口限行备案流程(海口限行备案)
- 下一篇: 安卓换头像的软件叫什么(安卓换头像)