ViennaNET: Könyvtárak készlete a backendhez. 2. rész

A .NET fejlesztők közössége a Raiffeisen Bankban folytatja a ViennaNET tartalmának rövid áttekintését. Arról, hogyan és miért jutottunk el idáig, a első részben olvasható.

Ebben a cikkben bemutatjuk még nem tárgyalt könyvtárakat, amelyek elosztott tranzakciókkal, üzenetsorokkal és adatbázisokkal kapcsolatosak, és amelyeket a GitHub tárhelyünkön találhatók (a források itt találhatók). Ezeknek az értékeknek a tudása lehetővé teszi számunkra, hogy megoldjuk az egyenletet a Nuget csomagok itt találhatók.

ViennaNET: Könyvtárak készlete a backendhez. 2. rész

ViennaNET.Sagas

Amikor a projektben áttérünk a DDD-ra és a mikroszolgáltatások architektúrájára, akkor a üzleti logika különböző szolgáltatásokra történő szétosztásakor felmerül a problémája, amely a szükséges mechanizmus megvalósításával kapcsolatos elosztott tranzakciókhoz, hiszen sok forgatókönyv gyakran több domaint is érint. Ezekkel a mechanizmusokkal részletesebben megismerkedhetünk például a „Microservices Patterns” című könyvben, Chris Richardson.

A projektjeinkben egyszerű, de hasznos mechanizmust valósítottunk meg: saga, pontosabban a vezérlés alapú saga. A lényege a következő: van egy üzleti forgatókönyv, amelyben szükséges lépésről lépésre végrehajtani műveleteket különböző szolgáltatásokban, és, ha bármely lépés során probléma merül fel, szükséges az összes előző lépés visszavonási eljárásának hívása, ahol azt előirányozták. Így, a saga végrehajtásának végén, a sikerességtől függetlenül, konzisztens adatokat kapunk az összes domainben.

A megvalósításunk jelenleg alap formában készült, és nem kötődik más szolgáltatásokkal való interakció bármely formájához. Alkalmazása nem nehéz: elég egy örökölt osztályt létrehozni az alap absztrakt osztályból SagaBase<T>, ahol T az az osztály, amely a saga működéséhez szükséges forrásadatokat, valamint néhány köztes eredményt tárolhat. A kontextus példányát minden lépésben továbbítják a végrehajtás során. Maga a saga egy stateless osztály, ezért a példányt a DI-be akár Singleton-ként is elhelyezhetjük a szükséges függőségek beszerzéséhez.

Példa nyilatkozat:

public class ExampleSaga : SagaBase<ExampleContext>
{
  public ExampleSaga()
  {
    Step("1. lépés")
      .WithAction(c => ...)
      .WithCompensation(c => ...);
	
    AsyncStep("2. lépés")
      .WithAction(async c => ...);
  }
}

Példa hívásra:

var saga = new ExampleSaga();
var context = new ExampleContext();
await saga.Execute(context);

A különböző megvalósítások teljes példái megtekinthetők itt és a gyűjteményben tesztekkel.

ViennaNET.Orm.*

Könyvtárkészlet különböző adatbázisokkal való munkához az Nhibernate-en keresztül. A DB-First megközelítést alkalmazzuk Liquibase segítségével, így itt csak az adatkezelési funkciók találhatók a kész adatbázisban.

ViennaNET.Orm.Seedwork és ViennaNET.Orm – fő összesítők, amelyek alapvető interfészeket és azok megvalósításait tartalmazzák. Részletesebben megvizsgáljuk a tartalmukat.

Felület IEntityFactoryService és annak megvalósítása EntityFactoryService a fő kiindulópont az adatbázissal való munkához, mivel itt jön létre az Unit of Work, az egyes entitásokhoz tartozó adattárak, valamint a parancsok és közvetlen SQL-lekérdezések végrehajtói. Néha hasznos korlátozni az adatbázissal való munkára vonatkozó osztály lehetőségeit, például csak az adatok olvasására engedélyt adni. Az ilyen esetekre van egy ős – az interfész IEntityFactoryService IEntityRepositoryFactory , amelyben csak a tárolók létrehozására vonatkozó módszer van nyilvánosítva.Az adatbázissal való közvetlen kapcsolatfelvételhez a szolgáltatók mechanizmusa használatos. Minden egyes adatbázis-kezelő rendszerhez, amelyet a csapataink használnak, van saját megvalósítása:

ViennaNET.Orm.MSSQL, ViennaNET.Orm.Oracle, ViennaNET.Orm.SQLite, ViennaNET.Orm.PostgreSql Ezenkívül egy alkalmazásban több szolgáltató is regisztrálható egyidejűleg, ami lehetővé teszi például, hogy egy szolgáltatáson belül, a infrastruktúra átalakítása nélküli lépésről lépésre történő migrációt végezzünk egy másik adatbázis-kezelő rendszerre. A szükséges kapcsolat és az adott osztály-entitás (amelyhez a táblákhoz való leképezés készült) szolgáltató kiválasztásának mechanizmusát az entitás regisztrálása valósítja meg a BoundedContext osztályban (amely tartalmazza a domainek entitásainak regisztrálására vonatkozó módszert), vagy annak örökölt ApplicationContext osztályában (amely az alkalmazási entitások, közvetlen lekérdezések és parancsok regisztrálásához szükséges módszereket tartalmazza), ahol az argumentum a konfigurációból származó kapcsolati azonosító..

"db": [ { "nick": "mssql_connection", "dbServerType": "MSSQL", "ConnectionString": "...", "useCallContext": true }, { "nick": "oracle_connection", "dbServerType": "Oracle", "ConnectionString": "..." } ],

ApplicationContext példa:

internal sealed class DbContext : ApplicationContext { public DbContext() { AddEntity<SomeEntity>("mssql_connection"); AddEntity<MigratedSomeEntity>("oracle_connection"); AddEntity<AnotherEntity>("oracle_connection"); } }

Ha a kapcsolati azonosító nincs megadva, akkor a "default" nevű kapcsolatot használja.

Ha a kapcsolati azonosító nincs megadva, akkor a "default" nevű kapcsolatot használja.

A lényegi entitás-tevékenység mappolása az adatbázis-táblákra az NHibernate szabványos eszközeivel valósul meg. Használható leírás XML fájlokon keresztül, valamint osztályok segítségével is. Az egységtesztekhez való segédlet repositorik egyszerű írásának érdekében elérhető egy könyvtár ViennaNET.TestUtils.Orm.

Teljes körű példák a ViennaNET.Orm.* használatáról találhatók itt.

ViennaNET.Messaging.*

Könyvtárak halmaza a várakozások kezelésére.

A várakozások kezeléséhez ugyanazt a megközelítést választottuk, mint a különböző DBMS-ek esetében, nevezetesen – a lehető legmaximálisabban egységes megközelítést az adott könyvtár használatára, függetlenül a használt várakozási menedzsertől. A könyvtár ViennaNET.Messaging pontosan ezt az egységesítést végzi, és a ViennaNET.Messaging.MQSeriesQueue, ViennaNET.Messaging.RabbitMQQueue és ViennaNET.Messaging.KafkaQueue megvalósításokat tartalmaz IBM MQ, RabbitMQ és Kafka adapterek számára.

A várakozásokkal való munkában két folyamat van: üzenet fogadása és küldése.

Nézzük a fogadást. Itt két lehetőség van: állandó figyelésre és egyedi üzenet fogadására. Állandó figyeléshez először egy processor osztályt kell leírni, amely öröklődik az IMessageProcessor, amely felelős a bejövő üzenetek feldolgozásáért. Ezt követően a megfelelő várakozáshoz kell „kötni”, amely a IQueueReactorFactory regisztrációjával történik, a konfigurációból származó várakozási azonosító megadásával:

"messaging": {
    "ApplicationName": "MyApplication"
},
"rabbitmq": {
    "queues": [
      {
        "id": "myQueue",
        "queuename": "lalala",
        ...
      }
    ]
},

Példa a figyelés indítására:

_queueReactorFactory.Register<MyMessageProcessor>("myQueue");
var queueReactor = queueReactorFactory.CreateQueueReactor("myQueue");
queueReactor.StartProcessing();

Ezután, amikor a szolgáltatás elindul, és a figyelés megkezdésére szolgáló metódust hívják meg, minden az adott várakozásból származó üzenet a megfelelő processzorhoz fog jutni.

Egyedi üzenet fogadásához az interfész-gyáron IMessagingComponentFactory van egy metódus CreateMessageReceiver, amely létrehozza a feladókat, akik a megadott várakozásból várják az üzenetet:

using (var receiver = _messagingComponentFactory.CreateMessageReceiver<TestMessage>("myQueue"))
{
    var message = receiver.Receive();
}

Üzenet küldéséhez ugyanazt a folyamatot kell használnunk IMessagingComponentFactory és létrehozni az üzenet küldőt:

using (var sender = _messagingComponentFactory.CreateMessageSender<MyMessage>("myQueue"))
{
    sender.SendMessage(new MyMessage { Value = ...});
}

Üzenetek sorozatba állításához és visszaolvasásához három előre elkészített megoldás áll rendelkezésre: egyszerű szöveg, XML és JSON, de igény szerint nyugodtan készíthetők saját interfészek is. IMessageSerializer és IMessageDeserializer.

Igyekeztünk megőrizni minden sorqueue manager egyedi lehetőségeit, például ViennaNET.Messaging.MQSeriesQueue lehetővé teszi nemcsak szöveges, hanem bájtos üzenetek küldését is, míg ViennaNET.Messaging.RabbitMQQueue támogatja a routing-ot és a „futtatási idejű” sorok létrehozását. RabbitMQ adapterünknél egy RPC-hez hasonló megoldás is megvalósításra került: üzenetet küldünk és választ várunk egy speciális ideiglenes sorból, amely csak egy válasz üzenethez hozható létre.

Íme példák a sorok használatára a főbb csatlakozási finomságokkal.

ViennaNET.CallContext

Sorokat nemcsak különböző rendszerek közötti integrációra használunk, hanem egy alkalmazáson belüli mikroszolgáltatások közötti kommunikációra is, például a sagák keretein belül. Ez arra vezetett, hogy az üzenettel együtt át kell adni olyan segédadatokat is, mint a felhasználó bejelentkezési neve, a kérés azonosítója a végig követéshez, a forrás IP-cím és az azonosító adatok. Ezeknek az adatoknak a továbbításához egy könyvtárat fejlesztettünk ki, ViennaNET.CallContext, amely lehetővé teszi a szolgáltatásra érkező kérésből származó adatok tárolását. Ebben az esetben nem számít, hogy a kérés hogyan érkezett, soron vagy Http-n át. Ezt követően, az kimenő kérés vagy üzenet elküldése előtt, az adatok kinyerésre kerülnek a kontextusból és a fejlécbe kerülnek. Így a következő szolgáltatás megkapja a segédadatokat és hasonlóképpen bánik velük.

Köszönjük a figyelmet, várjuk észrevételeiket és pull request-eiket!

Forrás: habr.com

Megbízható hosting vásárlása weboldalak számára, DDoS védelemmel, VPS VDS szerverek. 🔥 Megbízható webtárhely vásárlása DDoS védelemmel, VPS VDS szerverek | ProHoster