ViennaNET: könyvtárak készlete a háttérrendszerhez. 2. rész

A Raiffeisenbank .NET fejlesztői közössége továbbra is röviden áttekinti a ViennaNET tartalmát. Arról, hogyan és miért jutottunk idáig, az első részt olvashatod.

Ebben a cikkben az elosztott tranzakciókkal, várólistákkal és adatbázisokkal való munkavégzéshez szükséges, még megfontolás tárgyát képező könyvtárakat tekintjük át, amelyek a GitHub tárhelyünkben találhatók (források itt vannak), és Nuget csomagok itt.

ViennaNET: könyvtárak készlete a háttérrendszerhez. 2. rész

ViennaNET.Sagas

Amikor egy projekt DDD-re és mikroszolgáltatási architektúrára vált, akkor amikor az üzleti logika különböző szolgáltatások között van elosztva, probléma merül fel az elosztott tranzakciós mechanizmus megvalósításának szükségességével kapcsolatban, mivel sok forgatókönyv gyakran több tartományt érint egyszerre. Az ilyen mechanizmusokkal részletesebben megismerkedhet, pl. Chris Richardson "Microservices Patterns" című könyvében.

Projektjeinkben egy egyszerű, de hasznos mechanizmust valósítottunk meg: egy saga, vagy inkább egy hangszerelés alapú saga. Lényege a következő: van egy bizonyos üzleti forgatókönyv, amelyben egymás után kell végrehajtani a műveleteket a különböző szolgáltatásokban, és bármilyen probléma esetén bármely lépésben meg kell hívni az összes korábbi lépés visszaállítási eljárását, ahol biztosított. Így a saga végén a sikertől függetlenül minden területen konzisztens adatokat kapunk.

Megvalósításunk továbbra is alapvető formában történik, és nem kötődik más szolgáltatásokkal való interakciós módszerek használatához. Használata nem nehéz: csak készítse el a SagaBase alap absztrakt osztály leszármazottját, ahol T a kontextus osztálya, amelyben tárolhatja a saga működéséhez szükséges kezdeti adatokat, valamint néhány köztes eredményt. A kontextuspéldány a végrehajtás során minden lépésre átkerül. A Saga maga egy állapot nélküli osztály, így a példányt Singletonként lehet elhelyezni a DI-ben, hogy megkapja a szükséges függőségeket.

Példahirdetés:

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

Példahívás:

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 összeszerelésben azzal tesztek.

ViennaNET.Orm.*

Könyvtárkészlet különféle adatbázisokkal való munkavégzéshez a Nhibernate segítségével. A Liquibase segítségével a DB-First megközelítést használjuk, így csak egy kész adatbázisban lévő adatokkal való munkavégzésre van lehetőség.

ViennaNET.Orm.Seedwork и ViennaNET.Orm – az alapvető interfészeket tartalmazó főösszeállítások és azok megvalósításai, ill. Nézzük meg részletesebben a tartalmukat.

felület IEntityFactoryService és annak végrehajtása EntityFactoryService az adatbázissal való munkavégzés fő kiindulópontja, hiszen itt jönnek létre a Munkaegység, az adott entitásokkal való munkavégzéshez szükséges tárolók, valamint a parancsok végrehajtói és a közvetlen SQL lekérdezések. Néha célszerű korlátozni egy osztály képességeit egy adatbázissal való munkavégzéshez, például annak érdekében, hogy lehetővé tegye csak az adatok olvasását. Ilyen esetekre IEntityFactoryService van egy ős - interfész IEntityRepositoryFactory, amely csak egy adattárak létrehozásának módszerét deklarálja.

Az adatbázis közvetlen eléréséhez a szolgáltatói mechanizmust használják. Minden csapatunkban használt DBMS-nek megvan a maga megvalósítása: ViennaNET.Orm.MSSQL, ViennaNET.Orm.Oracle, ViennaNET.Orm.SQLite, ViennaNET.Orm.PostgreSql.

Ugyanakkor egyszerre több szolgáltató is regisztrálható egy alkalmazásban, ami lehetővé teszi például egy szolgáltatás keretén belül, az infrastruktúra módosításának költsége nélkül lépésről lépésre történő migrációt egyik DBMS-ről a másikra. A szükséges kapcsolat és így egy adott entitásosztály szolgáltatójának kiválasztásának mechanizmusa (amelyhez adatbázistáblázatokhoz való leképezés van írva) úgy valósul meg, hogy az entitást a BoundedContext osztályba (amely egy tartományi entitások regisztrálására szolgáló metódust tartalmaz) vagy annak utódjába regisztrálják. ApplicationContext (az alkalmazás entitások regisztrálásának metódusait, közvetlen kéréseket és parancsokat tartalmazza), ahol a konfigurációból származó kapcsolatazonosító elfogadásra kerül argumentumként:

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

Példa ApplicationContext:

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

Ha a csatlakozási azonosító nincs megadva, akkor a „default” nevű kapcsolat kerül felhasználásra.

Az entitások adatbázistáblákhoz való közvetlen hozzárendelése szabványos NHibernate eszközökkel valósul meg. A leírást xml-fájlokon és osztályokon keresztül egyaránt használhatja. Az egységtesztekben a csonktárolók kényelmes írásához van egy könyvtár ViennaNET.TestUtils.Orm.

A ViennaNET.Orm.* használatára teljes példák találhatók itt.

ViennaNET.Messaging.*

Könyvtárak készlete a sorokkal való munkához.

A sorokkal való munkához ugyanazt a megközelítést választottuk, mint a különféle DBMS-eknél, nevezetesen a lehető legnagyobb egységes megközelítést a könyvtárral való munkavégzés szempontjából, függetlenül a használt sorkezelőtől. Könyvtár ViennaNET.Messaging pontosan felelős ezért az egyesülésért, és ViennaNET.Messaging.MQSeriesQueue, ViennaNET.Messaging.RabbitMQQueue и ViennaNET.Messaging.KafkaQueue adapter implementációkat tartalmaznak az IBM MQ, RabbitMQ és Kafka számára.

A sorokkal való munka során két folyamat van: egy üzenet fogadása és elküldése.

Fontolja meg az átvételt. Itt 2 lehetőség van: folyamatos hallgatásra és egyetlen üzenet fogadására. A sor folyamatos figyeléséhez először le kell írnia a processzorosztályt, amelyből örökölt IMessageProcessor, amely a bejövő üzenet feldolgozásáért lesz felelős. Ezután egy adott sorhoz kell „kapcsolni”; ez regisztrációval történik IQueueReactorFactory a sor azonosítójának megadása a konfigurációból:

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

Példa a hallgatás megkezdésére:

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

Ezután, amikor a szolgáltatás elindul, és a metódus meghívásra kerül a figyelés megkezdéséhez, a megadott sor összes üzenete a megfelelő processzorhoz kerül.

Egyetlen üzenet fogadása gyári felületen IMessagingComponentFactory van egy módszer CreateMessageReceiveramely létrehoz egy címzettet, aki üzenetre vár a neki megadott sorból:

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

Üzenet küldéséhez ugyanazt kell használnod IMessagingComponentFactory és hozzon létre egy üzenetküldőt:

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

Három kész lehetőség van egy üzenet szerializálására és deszerializálására: csak szöveg, XML és JSON, de szükség esetén könnyedén elkészítheti saját interfész implementációit. IMessageSerializer и IMessageDeserializer.

Igyekeztünk megőrizni az egyes sorkezelők egyedi képességeit, pl. ViennaNET.Messaging.MQSeriesQueue nem csak szöveges, hanem bájtos üzenetek küldését is lehetővé teszi, ill ViennaNET.Messaging.RabbitMQQueue támogatja az útválasztást és az on-the-fly sorban állást. A RabbitMQ-hoz készült adapterburkolónk is megvalósítja az RPC némi látszatát: üzenetet küldünk, és egy speciális ideiglenes sorból várunk választ, amely csak egy válaszüzenethez jön létre.

Itt példa a sorok használatára alapvető kapcsolódási árnyalatokkal.

ViennaNET.CallContext

A sorokat nem csak a különböző rendszerek közötti integrációhoz használjuk, hanem ugyanazon alkalmazás mikroszolgáltatásai közötti kommunikációhoz is, például egy sagán belül. Ez ahhoz vezetett, hogy az üzenettel együtt olyan segédadatokat kellett továbbítani, mint a felhasználói bejelentkezés, a végpontok közötti naplózás kérési azonosítója, a forrás IP-címe és az engedélyezési adatok. Ezen adatok továbbításának megvalósítására könyvtárat fejlesztettünk ki ViennaNET.CallContext, amely lehetővé teszi a szolgáltatásba belépő kérés adatainak tárolását. Ebben az esetben nem számít, hogy a kérés hogyan történt, soron keresztül vagy Http-n keresztül. Ezután a kimenő kérés vagy üzenet elküldése előtt a rendszer az adatokat a kontextusból veszi és a fejlécekbe helyezi. Így a következő szolgáltatás fogadja a segédadatokat, és ugyanúgy kezeli azokat.

Köszönjük figyelmüket, várjuk észrevételeiket, kéréseiteket!

Forrás: will.com

Hozzászólás