Bevezetés a projekt-reaktor buszba

1. Áttekintés

Ebben a rövid cikkben bemutatjuk a reaktor-buszt egy valós forgatókönyv létrehozásával egy reaktív, eseményvezérelt alkalmazás számára.

2. A projektreaktor alapjai

2.1. Miért a Reaktor?

A modern alkalmazásoknak rengeteg egyidejű kérést kell kezelniük, és jelentős mennyiségű adatot kell feldolgozniuk. A szabványos blokkoló kód már nem elegendő e követelmények teljesítéséhez.

A reaktív tervezési minta egy eseményalapú építészeti megközelítés nagy mennyiségű egyidejű szolgáltatási kérelem aszinkron kezeléséhez egy vagy több szervizkezelőtől érkezik.

A Projektreaktor erre a mintára épül, és egyértelmű és ambiciózus célja a blokkolás nélküli, reaktív alkalmazások felépítése a JVM-en.

2.2. Példa forgatókönyvek

Mielőtt belekezdenénk, íme néhány érdekes forgatókönyv, ahol a reaktív építészeti stílus kiaknázása lenne értelme, csak hogy képet kapjunk arról, hogy hol alkalmazhatnánk:

  • Értesítési szolgáltatások olyan nagy online vásárlási platformhoz, mint az Amazon
  • Hatalmas tranzakciófeldolgozási szolgáltatások a bankszektor számára
  • Részvények olyan kereskedési vállalkozások számára, ahol a részvények ára egyszerre változik

3. Maven-függőségek

Kezdjük el használni a Project Reactor Bus alkalmazást azáltal, hogy hozzáadjuk a következő függőséget pom.xml:

 io.projectreaktor reaktor-busz 2.0.8 

A legújabb verzióját ellenőrizhetjük reaktor-busz a Maven Central-ban.

4. Demo alkalmazás készítése

A reaktor-alapú megközelítés előnyeinek jobb megértése érdekében nézzünk meg egy gyakorlati példát.

Készítünk egy egyszerű alkalmazást, amely felelős az értesítések küldéséért egy online vásárlási platform felhasználóinak. Például, ha egy felhasználó új megrendelést ad le, akkor az alkalmazás e-mailben vagy SMS-ben küldi el a megrendelés visszaigazolását.

A tipikus szinkron megvalósítást természetesen korlátozza az e-mail vagy az SMS szolgáltatás átviteli sebessége. Ezért a forgalmi csúcsok, például az ünnepek általában problémásak lennének.

Reaktív megközelítéssel úgy tudjuk megtervezni a rendszerünket, hogy rugalmasabb legyen, és jobban alkalmazkodjon a külső rendszerekben, például az átjárókiszolgálóknál előforduló hibákhoz vagy időtúllépésekhez.

Vessünk egy pillantást az alkalmazásra - kezdve a hagyományosabb szempontokkal és áttérve a reaktívabb konstrukciókra.

4.1. Egyszerű POJO

Először hozzunk létre egy POJO osztályt, amely az értesítési adatokat ábrázolja:

public class NotificationData {private long id; privát karakterlánc neve; privát karakterlánc e-mail; privát String mobil; // getter és setter módszerek}

4.2. A szolgáltatási réteg

Most definiáljunk egy egyszerű szolgáltatási réteget:

nyilvános felület NotificationService {void initiateNotification (NotificationData notificationData) dobja az InterruptedException-t; }

És a megvalósítás, hosszú távú műveletet szimulálva:

@Service public class NotificationServiceimpl implementálja a NotificationService {@Orride public void initiateNotification (NotificationData notificationData) dobja az InterruptedException {System.out.println ("Értesítési szolgáltatás elindítva" + "Értesítési azonosító:" + értesítésData.getId ()); Szál.alszik (5000); System.out.println ("Az értesítési szolgáltatás lejárt a" + "Értesítési azonosító:" + értesítésData.getId () esetén); }}

Figyelje meg, hogy az üzenetek SMS-ben vagy e-mail átjárón keresztül történő valós forgatókönyvének szemléltetése érdekében szándékosan öt másodperces késleltetést vezetünk be a initiateNotification módszerrel Szál.alszik (5000).

Következésképpen, amikor egy szál eléri a szolgáltatást, öt másodpercre blokkolja.

4.3. A Fogyasztó

Most térjünk át alkalmazásunk reaktívabb aspektusaira, és valósítsunk meg egy fogyasztót - amelyet aztán a reaktor eseménysínjére térképezünk fel:

@Service public class NotificationConsumer megvalósítja a Consumer-t {@Autowired private NotificationService notificationService; @Orride public void accept (Event notificationDataEvent) {NotificationData értesítésData = értesítésDataEvent.getData (); próbáld ki az {értesítésService.initiateNotification (értesítésData); } catch (InterruptedException e) {// figyelmen kívül hagyja}}}

Mint láthatjuk, az általunk létrehozott fogyasztó megvalósítja a Fogyasztó felület. A fő logika a elfogad módszer.

Hasonló megközelítéssel találkozhatunk egy tipikus tavaszi hallgatói megvalósítás során.

4.4. A Vezérlő

Végül, most, hogy el tudjuk fogyasztani az eseményeket, generáljuk őket is.

Ezt egy egyszerű vezérlővel fogjuk megtenni:

@Controller public class NotificationController {@Autowired private EventBus eventBus; @GetMapping ("/ startNotification / {param}") public void startNotification (@PathVariable Integer param) {for (int i = 0; i <param; i ++) {NotificationData data = new NotificationData (); data.setId (i); eventBus.notify ("notificationConsumer", Event.wrap (adatok)); System.out.println ("Értesítés" + i + ": az értesítési feladat sikeresen elküldve"); }}}

Ez meglehetősen magától értetődő - eseményeket közvetítünk a EventBus itt.

Például, ha egy kliens tíz param értékkel eléri az URL-t, akkor az eseménybuszon keresztül tíz esemény kerül elküldésre.

4.5. A Java Config

Most állítsunk össze mindent, és hozzunk létre egy egyszerű Spring Boot alkalmazást.

Először konfigurálnunk kell EventBus és Környezet bab:

@Configuration public class Config {@Bean public Environment env () {return Environment.initializeIfEmpty (). AssignErrorJournal (); } @Bean public EventBus createEventBus (Környezet env) {return EventBus.create (env, Környezet.THREAD_POOL); }}

A mi esetünkben, példányosítjuk a EventBus a környezetben elérhető alapértelmezett szálkészlettel.

Alternatív megoldásként használhatunk testreszabott Diszpécser példa:

EventBus evBus = EventBus.create (env, Environment.newDispatcher (REACTOR_CAPACITY, REACTOR_CONSUMERS_COUNT, DispatcherType.THREAD_POOL_EXECUTOR));

Most készen állunk egy fő alkalmazáskód létrehozására:

statikus reaktor importálása.bus.selector.Selectors. $; @SpringBootApplication public class NotificationApplication a CommandLineRunner {@Autowired private EventBus eventBus alkalmazást hajtja végre; @Autowired private NotificationConsumer notificationConsumer; A @Orride public void run (String ... args) a (z) {eventBus.on ($ ("értesítésiFogyasztó"), értesítésFogyasztó) kivételt dobja; } public static void main (String [] args) {SpringApplication.run (NotificationApplication.class, args); }}

Miénkben fuss módszer regisztráljuk a értesítésFogyasztó akkor kell elindítani, amikor az értesítés megegyezik egy adott választóval.

Figyelje meg, hogyan használjuk a $ attribútum a létrehozásához Választó tárgy.

5. Tesztelje az alkalmazást

Készítsünk most egy tesztet, hogy lássuk NotificationApplication működés közben:

@RunWith (SpringRunner.class) @SpringBootTest (webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class NotificationApplicationIntegrationTest {@LocalServerPort private int port; @Test public void givenAppStarted_whenNotificationTasksSubended_thenProcessed () {RestTemplate restTemplate = new RestTemplate (); restTemplate.getForObject ("// localhost:" + port + "/ startNotification / 10", String.osztály); }}

Mint láthatjuk, amint a kérés teljesül, mind a tíz a feladatokat blokkolás nélkül azonnal elküldik. Miután benyújtották, az értesítési eseményeket párhuzamosan dolgozzák fel.

0. értesítés: az értesítési feladat sikeresen elküldve 1. értesítés: az értesítési feladat sikeresen elküldve 2. értesítés: az értesítési feladat sikeresen elküldve 3. értesítés: az értesítési feladat sikeresen elküldve 4. értesítés: az értesítési feladat sikeresen elküldve 5. értesítés: az értesítési feladat sikeresen elküldve 5. értesítés: az értesítési feladat sikeresen elküldve Értesítés 7: értesítési feladat sikeresen elküldve 8. értesítés: értesítési feladat sikeresen elküldve 9. értesítés: értesítési feladat sikeresen elküldve Értesítési szolgáltatás elindult az Értesítési azonosítóhoz: 1 Értesítési szolgáltatás elindult az Értesítési azonosítóhoz: 2 Értesítési szolgáltatás elindult az Értesítési azonosítóhoz: 3 Értesítési szolgáltatás elindul az Értesítési azonosítóhoz : 0 Értesítési szolgáltatás befejeződött az Értesítési azonosítóhoz: 1 Értesítési szolgáltatás befejeződött az Értesítési azonosítóhoz: 0 Értesítési szolgáltatás elindult az Értesítési azonosítóhoz: 4 Értesítési szolgáltatás befejeződött az Értesítési azonosítóhoz: 3 Értesítési szolgáltatás lejárt az értesítési azonosítóra: 2 Értesítési szolgáltatás elindult az értesítés azonosítójára: 6 értesítési szolgáltatás elindult az értesítés azonosítójára: 5 értesítési szolgáltatás elindult az értesítés azonosítójára: 7 értesítési szolgáltatás elindult az értesítés azonosítójára: 4 Értesítési azonosító: 6 Értesítési szolgáltatás befejeződött az Értesítési azonosító esetében: 5 Értesítési szolgáltatás elindult az Értesítési azonosítóhoz: 9 Az értesítési szolgáltatás befejeződött az Értesítési azonosítóhoz: 7 Az értesítési szolgáltatás befejeződött az Értesítési azonosítóhoz: 8

Fontos ezt szem előtt tartani forgatókönyvünk szerint nem szükséges ezeket az eseményeket külön sorrendben feldolgozni.

6. Következtetés

Ebben a gyors bemutatóban létrehoztunk egy egyszerű eseményvezérelt alkalmazást. Láttuk azt is, hogyan kezdjünk el egy reaktívabb és nem blokkolóbb kódot írni.

Azonban, ez a forgatókönyv csak megkarcolja a téma felületét, és csak jó alapot jelent a reaktív paradigma kísérletezéséhez.

Mint mindig, a forráskód is elérhető a GitHubon.