V tomto jednoduchém tutoriálu vytvoříme několik mikroslužeb na Spring Boot a zorganizujeme jejich interakci prostřednictvím frameworku Axon.

Předpokládejme, že máme takový úkol.
Existuje zdroj obchodů na akciovém trhu. Tento zdroj nám předává obchody prostřednictvím REST rozhraní.
Musíme tyto obchody získat, uložit do databáze a vytvořit pohodlné in-memory úložiště.
Toto úložiště by mělo vykonávat následující funkce:
- vracet seznam obchodů;
- vracet úplnou pozici, tj. tabulku "nástroj" — "aktuální počet cenných papírů";
- vracet pozici podle daného nástroje.
Jak přistoupíme k řešení tohoto úkolu?
Podle zásad mikroslužeb musíme úkol rozdělit na jednotlivé mikroslužby:
- získání obchodu přes REST;
- uložení obchodu do databáze;
- in-memory úložiště pro prezentaci dat o pozici.
Pojďme v rámci tohoto tutoriálu vytvořit první a třetí službu, druhou necháme na druhou část (napište do komentářů, pokud je to zajímavé).
Takže máme dvě mikroslužby.
První získává data zvenčí.
Druhá tato data zpracovává a odpovídá na příchozí požadavky.
Samozřejmě chceme dosáhnout horizontálního škálování, bezproblémových aktualizací a dalších výhod mikroslužeb.
Jaký, velmi komplikovaný, úkol před námi stojí?
Ve skutečnosti je jich mnoho, ale teď se pojďme pobavit o tom, jak budou mezi těmito mikroslužbami proudit data. Mezi nimi lze také vytvořit REST, můžete použít nějakou frontu, můžete s mnoha věcmi přijít se svými plusy a mínusy.
Podívejme se na jeden z možných přístupů – asynchronní interakci prostřednictvím frameworku Axon.
Jaké výhody toto řešení přináší?
Za prvé, asynchronní interakce zvyšuje flexibilitu (ano, zde je také nevýhoda, ale nyní jsme právě jen o výhodách).
Za druhé, dostáváme přímo z krabice Event Sourcing a CQRS.
Za třetí, Axon poskytuje hotovou infrastrukturu, a my se musíme soustředit pouze na vývoj obchodní logiky.
Začněme.
Projekt bude na Gradlu. Bude mít tři moduly:
- common. modul s obecnými datovými strukturami (nemáme rádi kopírování);
- tradeCreator. modul s mikroslužbou pro přijímání obchodů přes REST;
- tradeQueries. modul s mikroslužbou pro zobrazení pozice.
Vezmeme Spring Boot jako základ a připojíme Axon starter.
Axon funguje skvěle i bez Springu, ale budeme je používat spolu.
Tady bychom se měli na chvíli zastavit a říct pár slov o Axonu.
Jedná se o klient-server systém. Je tu server – to je samostatná aplikace, kterou spustíme v Dockeru.
A jsou tu klienti, kteří se integrují do mikroservisů.
Vytváří se takový obrázek. Nejprve se spustí Axon server (v Dockeru), a poté naše mikroservisy.
Při spuštění mikroservisy hledají server a začínají s ním komunikovat. Komunikaci můžeme podmíněně rozdělit na dva typy: technickou a obchodní.
Technická – to je výměna zpráv typu 'jsem naživu' (takové zprávy lze vidět v režimu debugování).
Obchodní – to je výměna zpráv typu 'nový obchod'.
Důležitá vlastnost: po spuštění se mikroservis může zeptat Axon serveru 'co se stalo' a server předává mikroservisu nahromaděné události. Takto lze mikroservis relativně bezpečně restartovat bez ztráty dat.
Při takovém schématu výměny můžeme velmi jednoduše spouštět mnoho instancí mikroservisů,
a to i na různých hostitelích.
Ano, jedna instance Axon serveru – to není spolehlivé, ale zatím je to tak.
Pracujeme v paradigma Event Sourcing a CQRS. To znamená, že musíme mít 'příkazy', 'události' a 'dotazy'.
Budeme mít jeden příkaz: 'vytvořit obchod', jednu událost 'obchod vytvořen' a tři dotazy: 'zobrazit všechny obchody', 'zobrazit pozici', 'zobrazit pozici podle nástroje'.
Schéma práce vypadá takto:
- Mikroservis tradeCreator přijímá obchod přes Rest.
- Mikroservis tradeCreator vytváří příkaz 'vytvořit obchod' a posílá jej do Axon serveru.
- Axon server přijímá příkaz a přeposílá ho zúčastněnému příjemci, v našem případě mikroservisu tradeCreator.
- Mikroservis tradeCreator obdrží příkaz, vytvoří událost 'obchod vytvořen' a odešle ji Axon serveru.
- Axon server přijímá událost a přeposílá ji zainteresovaným odběratelům.
- V současnosti máme jen jednoho zainteresovaného příjemce – mikroservis tradeQueries.
- Mikroservis tradeQueries obdrží událost a aktualizuje interní data.
(Důležité je, že v okamžiku vytvoření události mikroservis tradeQueries nemusí být dostupný, ale jakmile se spustí, okamžitě obdrží událost).
Ano, Axon server stojí v centru komunikací, všechny zprávy procházejí přes něj.
Přejděme k programování.
Abychom nezahltili příspěvek kódem, níže uvedu pouze fragmenty, odkaz na celý příklad bude níže.
Začneme s obecným modulem common.
V něm jsou společné části – událost (class CreatedTradeEvent). Všimněte si pojmenování, v podstatě to je název příkazu, který tuto událost vyvolal, ale v minulém čase. V minulém čase, protože nejprve se objeví příkaz, který vede k vytvoření události.
Mezi další obecné struktury patří třídy pro popis pozice (class Position), obchodu (class Trade) a strany obchodu (enum Side), tj. koupě nebo prodej.
Přejdeme k modulu tradeCreator.
Tento modul má REST rozhraní (class TradeController) pro přijímání obchodů.
Ze získaného obchodu se vytváří příkaz „vytvořit obchod“ a je odeslán na Axon server.
@PostMapping("/trade")
public ResponseEntity create(@RequestBody Trade trade) {
var createTradeCommand = CreateTradeCommand.builder()
.tradeId(trade.getTradeId())
...
.build();
var result = commandGateway.sendAndWait(createTradeCommand, 3, TimeUnit.SECONDS);
return ResponseEntity.ok(result.get().toString());
}
Pro zpracování příkazu se používá třída class TradeAggregate.
Aby ho Axon našel, přidáme anotaci @Aggregate.
Metoda pro zpracování příkazu vypadá takto (s výsečí):
@CommandHandler
public TradeAggregate(CreateTradeCommand command) {
log.info("command: {}", command);
var event = CreatedTradeEvent.builder()
.tradeId(command.tradeId())
....
.build();
AggregateLifecycle.apply(event);
}
Z příkazu se vytváří událost a odesílá se na server.
Příkaz se nachází ve třídě CreateTradeCommand.
Teď se podíváme na poslední modul tradeQueries.
Dotazy jsou popsány v balíčku queries.
V tomto modulu také existuje REST rozhraní
public class TradeController.
Pro příklad se podíváme na zpracování dotazu: „zobrazit všechny obchody“.
@GetMapping("/trade/all")
public List findAllTrades() {
return queryGateway.query(new FindAllTradesQuery(),
ResponseTypes.multipleInstancesOf(Trade.class)).join();
}
Vytváří se dotaz na výběr a odesílá se na server.
Pro zpracování dotazu na výběr se používá třída TradesEventHandler.
V ní je metoda, označená anotací
@QueryHandler
public List handleFindCurrentPositionQuery(FindCurrentPositionQuery query)
Ta je zodpovědná za výběr dat z in-memory úložiště.
Vybavuje se otázka, jak se v tomto úložišti aktualizují informace.
Začneme tím, že to je prostě sada ConcurrentHashMap, přizpůsobených pro konkrétní výběry.
Pro jejich aktualizaci se používá metoda:
@EventHandler
public void on(CreatedTradeEvent event) {
log.info("event:{}", event);
var trade = Trade.builder()
...
.build();
trades.put(event.tradeId(), trade);
position.merge(event.shortName(), event.size(),
(oldValue, value) -> event.side() == Side.BUY ? oldValue + value : oldValue - value);
}Zpracovává událost „obchod vytvořen“ a aktualizuje Mapy.
To jsou hlavní body vývoje mikroslužeb.
Co lze říci o nevýhodách Axonu?
Za prvé, komplikuje se infrastruktura, vzniká bod selhání – Axon server, veškerá komunikace probíhá přes něj.
Za druhé, výrazně se projevuje nevýhoda podobných distribuovaných systémů – dočasná nekonzistentnost dat. V našem případě může mezi získáním nové smlouvy a aktualizací dat pro výběry uplynout nepřijatelně dlouhá doba.
Co zůstalo za oponou?
O Event Sourcing a CQRS nebylo vůbec řečeno, co to je a k čemu je to potřeba.
Bez vysvětlení těchto pojmů mohlo několik bodů zůstat nejasných.
Možná některé fragmenty kódu také vyžadují vysvětlení.
O tom si promluvíme na 21. září.
.
Zdroj: habr.com
