I denna enkla tutorial kommer vi att skapa ett par mikrotjänster med Spring Boot och organisera kommunikationen mellan dem via Axon-ramverket.

Anta att vi har följande uppgift.
Det finns en källa för affärer på aktiemarknaden. Denna källa överför affärer till oss via ett REST-gränssnitt.
Vi måste hämta dessa affärer, spara dem i en databas och skapa en praktisk in-memory lagring.
Detta lagringssystem skall uppfylla följande funktioner:
- återge en lista över affärer;
- återge den fullständiga positionen, det vill säga en tabell med "instrument" – "aktuellt antal papper";
- återge positionen för ett givet instrument.
Hur kommer vi att närma oss lösningen på denna uppgift?
Enligt mikrotjänsternas principer måste vi dela upp uppgiften i olika mikrotjänster:
- hämtning av affärer via REST;
- spara affärer i en databas;
- in-memory lagring för att representera data för positionen.
Låt oss i den här tutorialen skapa den första och den tredje tjänsten, medan vi lämnar den andra till del två (skriv i kommentarerna om det är av intresse).
Så, vi har två mikrotjänster.
Den första hämtar data utifrån.
Den andra behandlar dessa data och svarar på inkommande förfrågningar.
Vi vill självklart uppnå horisontell skalbarhet, felfri uppdatering och andra fördelar med mikrotjänster.
Vilken, ganska svår, uppgift står vi inför?
Det finns faktiskt många, men låt oss just nu diskutera hur data ska flöda mellan dessa mikrotjänster. Vi kan också skapa REST mellan dem, sätta upp någon typ av kö, eller hitta på mycket annat med sina egna fördelar och nackdelar.
Låt oss titta på en möjlig metod – asynkron interaktion via Axon-ramverket.
Vilka fördelar har en sådan lösning?
För det första, asynkron interaktion ökar flexibiliteten (ja, det finns en nackdel här, men vi pratar bara om fördelarna just nu).
För det andra får vi direkt ur lådan Event Sourcing och CQRS.
För det tredje tillhandahåller Axon en färdig infrastruktur, och vi behöver bara fokusera på att utveckla affärslogiken.
Låt oss börja.
Projektet kommer att vara på Gradle. Det kommer att ha tre moduler:
- common. modul med gemensamma datatyper (vi gillar inte att kopiera och klistra);
- tradeCreator. modul med mikrotjänsten för att ta emot affärer via REST;
- tradeQueries. modul med mikrotjänsten för att visa positionen.
Vi tar Spring Boot som grund och kopplar in Axon-starter.
Axon fungerar utmärkt även utan Spring, men vi kommer att använda dem tillsammans.
Här behöver vi stanna upp och säga några ord om Axon.
Detta är ett klient-server-system. Det finns en server – en separat applikation som vi kommer att köra i Docker.
Och det finns klienter som integreras i mikrotjänster.
Det blir en sådan bild. Först startas Axon-servern (i Docker), och sedan våra mikrotjänster.
Vid start letar mikrotjänsterna efter servern och börjar interagera med den. Interaktionen kan grovt delas in i två typer: teknisk och affärsmässig.
Den tekniska – det är meddelandeutbyte som "jag är levande" (sådana meddelanden kan ses i debugloggningen).
Den affärsmässiga – det handlar om meddelanden som "ny affär".
En viktig egenskap, efter att mikrotjänsten har startats kan den fråga Axon-servern "vad har hänt" och servern vidarebefordrar de insamlade händelserna till mikrotjänsten. På så sätt kan mikrotjänsten relativt säkert startas om utan att data går förlorad.
Med denna utbytesstruktur kan vi enkelt starta många instanser av mikrotjänster,
även på olika värdar.
Ja, en instans av Axon-servern är inte pålitlig, men så länge är det så här.
Vi arbetar inom paradigmerna Event Sourcing och CQRS. Det innebär att vi måste ha "kommandon", "händelser" och "frågor".
Vi kommer att ha ett kommando: "skapa affär", en händelse "affär skapad" och tre frågor: "visa alla affärer", "visa position", "visa position per instrument".
Arbetsflödet ser ut så här:
- Mikrotjänsten tradeCreator tar emot affären via Rest.
- Mikrotjänsten tradeCreator skapar kommandot "skapa affär" och skickar det till Axon-servern.
- Axon-servern tar emot kommandot och vidarebefordrar det till den intresserade mottagaren, i vårt fall mikrotjänsten tradeCreator.
- Mikrotjänsten tradeCreator får kommandot, formar händelsen "affär skapad" och skickar den till Axon-servern.
- Axon-servern tar emot händelsen och vidarebefordrar den till de intresserade prenumeranterna.
- För tillfället har vi bara en intresserad mottagare – mikrotjänsten tradeQueries.
- Mikrotjänsten tradeQueries får händelsen och uppdaterar sina interna data.
(Det är viktigt att notera att mikrotjänsten tradeQueries kan vara otillgänglig när händelsen skapas, men så snart den startas kommer den omedelbart att få händelsen).
Ja, axon-servern står i centrum för kommunikationen, alla meddelanden går genom den.
Låt oss gå vidare till kodning.
För att inte överbelasta inlägget med kod kommer jag nedan endast att ge fragment, länken till hela exemplet kommer att finnas nedan.
Vi börjar med den allmänna modulen common.
I den finns de allmänna delarna – detta event (klass CreatedTradeEvent). Observera namnet, detta är i princip namnet på kommandot som skapade detta event, men i förfluten tid. I förfluten tid eftersom kommandot först uppträder och leder till skapandet av eventet.
Andra gemensamma strukturer inkluderar klasser för att beskriva position (klass Position), affär (klass Trade) och affärens sida (enum Side), dvs. köp eller försäljning.
Vi går vidare till modulen tradeCreator.
Denna modul har ett Rest-gränssnitt (klass TradeController) för att ta emot affärer.
Från den mottagna affären formuleras kommandot "skapa affär" och skickas till axon-servern.
@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());
}
För att behandla kommandot används klassen class TradeAggregate.
För att Axon ska hitta den, lägger vi till annotationen @Aggregate.
Metoden för att behandla kommandot ser ut så här (med förkortning):
@CommandHandler
public TradeAggregate(CreateTradeCommand command) {
log.info("command: {}", command);
var event = CreatedTradeEvent.builder()
.tradeId(command.tradeId())
....
.build();
AggregateLifecycle.apply(event);
}
Från kommandot skapas ett event och skickas till servern.
Kommandot finns i klassen CreateTradeCommand.
Nu tittar vi på den sista modulen tradeQueries.
Frågor beskrivs i paketet queries.
Denna modul har också ett Rest-gränssnitt
public class TradeController.
Som exempel ser vi på behandlingen av frågan: "visa alla affärer".
@GetMapping("/trade/all")
public List findAllTrades() {
return queryGateway.query(new FindAllTradesQuery(),
ResponseTypes.multipleInstancesOf(Trade.class)).join();
}
En förfrågan om datauttag skapas och skickas till servern.
För att bearbeta förfrågan används klassen TradesEventHandler.
I den finns en metod som markerats med annotationen
@QueryHandler
public List handleFindCurrentPositionQuery(FindCurrentPositionQuery query)
Det är just denna som är ansvarig för att hämta data från in-memory lagret.
Frågan uppstår, hur uppdateras informationen i detta lagret.
Vi börjar med att säga att det helt enkelt är en uppsättning ConcurrentHashMap, anpassade för specifika uttag.
För att uppdatera dem används metoden:
@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);
}Det tar emot händelsen 'handel skapad' och uppdaterar Map-objekten.
Detta är huvudpunkterna i utvecklingen av mikrotjänster.
Vad kan sägas om nackdelarna med Axon?
För det första, det komplicerar infrastrukturen; en felpunkt dyker upp – Axon-servern, alla kommunikationer går genom den.
För det andra, en tydlig nackdel med sådana distribuerade system är den temporära inkonsekvensen i data. I vårt fall kan det ta oacceptabelt lång tid mellan att en ny handel tas emot och att datan för urvalen uppdateras.
Vad kvarstår i bakgrunden?
Ingenting har sagts om Event Sourcing och CQRS, vad det är och varför det behövs.
Utan att förklara dessa begrepp kan vissa punkter vara oklara.
Kanske behöver vissa kodfragment också förtydligas.
Detta kommer vi att diskutera på 21 september.
.
Källa: habr.com
