Mikroshërbimet me komunikim përmes Axon

Në këtë tutorial të thjeshtë, ne do të krijojmë disa mikrosherbime në Spring Boot dhe do të organizojmë ndërveprimin midis tyre përmes kuadrit Axon.

Mikroshërbimet me komunikim përmes Axon


Supozoni se kemi një detyrë të tillë.

Ka një burim të tregtisë në tregun e aksioneve. Ky burim na përcjell tregtitë përmes një interfacesh Rest.

Na nevojitet tĂ« marrim kĂ«to tregti, t’i ruajmĂ« nĂ« njĂ« bazĂ« tĂ« dhĂ«nash dhe tĂ« krijojmĂ« njĂ« ruajtje tĂ« pĂ«rshtatshme nĂ« memorien e serverit.

Kjo ruajtje duhet të kryejë funksionet e mëposhtme:

  • tĂ« kthejĂ« njĂ« listĂ« tregtish;
  • tĂ« kthejĂ« pozitat e plota, dmth. njĂ« tabelĂ« "instrument" — "numri aktual i aksioneve";
  • tĂ« kthejĂ« pozitat sipas njĂ« instrumenti tĂ« caktuar.

Si do t'i qasemi zgjidhjes së kësaj detyre?

Sipas parimeve të mikrosherbimeve, na nevojitet të ndajmë detyrën në mikrosherbime përbërëse:

  • marrja e tregtisĂ« pĂ«rmes Rest;
  • ruajtja e tregtisĂ« nĂ« njĂ« bazĂ« tĂ« dhĂ«nash;
  • ruajtje nĂ« memorien e serverit pĂ«r paraqitjen e tĂ« dhĂ«nave tĂ« pozitat.

Le të krijojmë në kuadër të këtij tutoriali shërbimin e parë dhe të tretë, ndërsa të dytin ta lëmë për pjesën e dytë (na shkruani në komente nëse kjo ju intereson).

Pra, ne kemi dy mikrosherbime.

I pari merr të dhënat nga jashtë.

I dyti i përpunon këto të dhëna dhe përgjigjet kërkesave hyrëse.

Sigurisht dëshirojmë të arrijmë shkallëzimin horizontal, rinovimin pa ndërprerje dhe përfitime të tjera të mikro_shërbimeve.

Cila është, në të vërtetë, një detyrë mjaft e vështirë që kemi përpara?

Në fakt, ka shumë, por tani le të flasim se si do të shkojnë të dhënat midis këtyre mikro_shërbimeve. Ndërmjet tyre mund të përdorim Rest, mund të vendosim ndonjë radhë, mund të imagjinojmë shumë gjëra me përfitimet dhe disavantazhet e tyre.

Le tĂ« shqyrtojmĂ« njĂ« nga qasjet e mundshme – ndĂ«rveprimin asinkron pĂ«rmes Axon-framework.

Cilat janë përfitimet e kësaj zgjidhjeje?

Së pari, ndërveprimi asinkron rrit fleksibilitetin (po, ka këtu edhe një disavantazh, por tani jemi duke folur vetëm për përfitimet).

SĂ« dyti, direkt nga kutia ne marrim Event Sourcing dhe CQRS.
Së treti, Axon ofron një infrastrukturë të gatshme, dhe ne duhet të përqendrohemi vetëm në zhvillimin e logjikës së biznesit.

Le të fillojmë.

Projekti ynë do të jetë në gradle. Në të do të ketë tre module:

  • common. moduli me strukturat e zakonshme tĂ« tĂ« dhĂ«nave (ne nuk i pĂ«lqejmĂ« kopjimet);
  • tradeCreator. moduli me mikro_shĂ«rbimin pĂ«r pranimin e transaksioneve pĂ«rmes Rest;
  • tradeQueries. moduli me mikro_shĂ«rbimin pĂ«r shfaqjen e pozicionit.

Do të marrim Spring Boot si bazë dhe do të lidhim starter-in e Axon-it.

Axon punon shkëlqyer edhe pa Spring, por ne do t'i përdorim ato së bashku.

Këtu duhet të ndalojmë dhe të flasim pak për Axon.

Kjo Ă«shtĂ« njĂ« sistem klient-server. Ka njĂ« server – Ă«shtĂ« njĂ« aplikacion i ndarĂ«, ne do ta çojmĂ« nĂ« docker.

Dhe ka klientë që integrohen në mikroshërbime.
Kështu kemi një imazh. Së pari, aktivizohet serveri Axon (në docker), pastaj mikroshërbimet tona.

Kur startojnë, mikroshërbimet kërkojnë serverin dhe fillojnë të bashkëveprojnë me të. Bashkëveprimi mund të ndahet në dy lloje: teknik dhe biznesor.

Tekniku – Ă«shtĂ« shkĂ«mbim mesazheve "jam gjallĂ«" (kĂ«to mesazhe mund tĂ« shihen nĂ« modalitetin e regjistrimit debug).

Biznesori – Ă«shtĂ« shkĂ«mbim mesazhesh si "transaksion i ri".

Një veçori e rëndësishme, pas aktivizimit mikroshërbimi mund të pyesë Axon-serverin "çfarë ndodhi" dhe serveri i dërgon mikroshërbimit ngjarjet e grumbulluara. Në këtë mënyrë, mikroshërbimi mund të rstartohet relativisht në siguri pa humbur të dhëna.
Me këtë skemë shkëmbimi ne mund të aktivizojmë shumë instanca të mikroshërbimeve shumë lehtë,
madje në hoste të ndryshëm.

Po, një instancë e vetme e Axon-serverit nuk është e besueshme, por për momentin kështu është.

Ne punojmë në paradigmat e Event Sourcing dhe CQRS. Kjo do të thotë se ne duhet të kemi "komanda", "ngjarje" dhe "selektime".

Kemi një komandë: "krijo biznesin", një ngjarje "biznesi i krijuar" dhe tri selektime: "trego të gjitha bizneset", "trego pozitat", "trego pozitat sipas mjetit".

Skema e punës del si kjo:

  1. Mikrositë tradeCreator pranon një biznes përmes Rest.
  2. Mikrositë tradeCreator krijon një komandë "krijo biznesin" dhe e dërgon atë në serverin Axon.
  3. Serveri Axon pranon komandën dhe e dërgon atë tek marrësi i interesuar, në rastin tonë, mikrositë tradeCreator.
  4. Mikrositë tradeCreator merr komandën, formon ngjarjen "biznes i krijuar" dhe e dërgon atë në serverin Axon.
  5. Serveri Axon pranon ngjarjen dhe e dërgon atë tek abonentët e interesuar.
  6. Tani kemi vetĂ«m njĂ« marrĂ«s tĂ« interesuar – Ă«shtĂ« mikrositĂ« tradeQueries.
  7. Mikrositë tradeQueries merr ngjarjen dhe përditëson të dhënat e brendshme.

(ËshtĂ« e rĂ«ndĂ«sishme qĂ« nĂ« momentin e formimit tĂ« ngjarjes mikrositĂ« tradeQueries mund tĂ« mos jetĂ« e qasshme, por sapo tĂ« nisĂ«, ajo do tĂ« marrĂ« menjĂ«herĂ« ngjarjen).

Po, serveri axon qëndron në qendër të komunikimeve, të gjitha mesazhet kalojnë përmes tij.

Le të kalojmë në kodim.

Për të mos e mbushur postimin me kode, më poshtë do të jap vetëm fragmente, linku për shembullin e plotë do të jetë më poshtë.

Të fillojmë me modulin e përbashkët common.

Në të, pjesët e përbashkëta janë ngjarja (class CreatedTradeEvent). Vini re emërtimin, në thelb, kjo është emri i komandës që kishte krijuar këtë ngjarje, por në kohën e kaluar. Në të kaluarën, pasi fillimisht shfaqet komanda që çon në krijimin e ngjarjes.

Strukturat e tjera të përbashkëta përfshijnë klasat për të përshkruar pozitat (class Position), transaksionet (class Trade) dhe anën e transaksionit (enum Side), pra blerje ose shitje.

Kalojmë në modulin tradeCreator.

Ky modul ka një ndërfaqe Rest (class TradeController) për të pranuar transaksione.
Nga transaksioni i pranuar formohet komanda "krijo transaksion" dhe dërgohet në serverin axon.

    @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());
    }

Për të përpunuar komandën përdoret klasa class TradeAggregate.
Për ta gjetur Axon-in, vendosim anotimin @Aggregate.
Metoda për trajtimin e komandës duket kështu (me shkurtime):

    @CommandHandler
    public TradeAggregate(CreateTradeCommand command) {
        log.info("komanda: {}", command);
        var event = CreatedTradeEvent.builder()
                .tradeId(command.tradeId())
		....
                .build();
        AggregateLifecycle.apply(event);
    }

Nga komanda formohet një ngjarje dhe dërgohet në server.
Komanda ndodhet në klasën CreateTradeCommand.

Tani le të shikojmë modulin e fundit tradeQueries.

Kërkesat përshkruhen në paketën queries.
Në këtë modul ka gjithashtu një ndërfaqe Rest
public class TradeController.

Për shembull, le t'i shohim trajtimi i kërkesës: "trego të gjitha tregjet".

    @GetMapping("/trade/all")
    public List findAllTrades() {
        return queryGateway.query(new FindAllTradesQuery(),
                ResponseTypes.multipleInstancesOf(Trade.class)).join();
    }

Skizohet një kërkesë për seleksionim dhe dërgohet në server.

Për trajtimin e kërkesës për seleksionim përdoret klasa TradesEventHandler.
Në të ka një metodë të shënuar me annotim

   @QueryHandler
    public List handleFindCurrentPositionQuery(FindCurrentPositionQuery query)

Ajo është përgjegjëse për seleksionimin e të dhënave nga ruajtja in-memory.

Ngrihet pyetja, si përditësohet informacioni në këtë ruajtje.

Të fillojmë me faktin se kjo është thjesht një grup i ConcurrentHashMap, i orientuar për seleksionime të caktuara.
Për përditësimin e tyre përdoret 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);
    }

Ai merr një ngjarje "tregtia e krijuar" dhe përditëson harta.

Këto janë pikat kryesore të zhvillimit të mikroservisave.

ÇfarĂ« mund tĂ« thuhet pĂ«r disavantazhet e Axon?

SĂ« pari, kjo e komplikon infrastrukturĂ«n, duke krijuar njĂ« pikĂ« tĂ« dĂ«shtimit – serverin Axon, tĂ« gjitha komunikimet kalojnĂ« pĂ«rmes tij.

SĂ« dyti, mangĂ«sia e tillĂ« e sistemeve tĂ« shpĂ«rndara shfaqet dukshĂ«m – moskoordinimi temporal i tĂ« dhĂ«nave. NĂ« rastin tonĂ«, midis pranimit tĂ« njĂ« tregtie tĂ« re dhe pĂ«rditĂ«simit tĂ« tĂ« dhĂ«nave pĂ«r seleksionet mund tĂ« kalojĂ« njĂ« kohĂ« shumĂ« e gjatĂ«.

ÇfarĂ« mbeti jashtĂ« skenĂ«s?

Nuk është thënë asgjë për Event Sourcing dhe CQRS, çfarë janë dhe për çfarë janë të nevojshme.
Pa shpjegimin e këtyre koncepteve, disa pika mund të mos kuptohen.

Mund të jetë se disa fragmente të kodit gjithashtu kërkojnë shpjegim.

Për këtë do të flasim në webinarin e hapur më 21 Shtator.

Shembulli i plotë.

Burimi: habr.com

Bli njĂ« hosting tĂ« besueshĂ«m pĂ«r faqet me mbrojtje DDoS, VPS VDS serverĂ« đŸ”„ Bli njĂ« hosting tĂ« besueshĂ«m pĂ«r faqet me mbrojtje DDoS, VPS VDS serverĂ« | ProHoster