Raiffeisenbank .NET 開發者社群繼續簡要回顧 ViennaNET 的內容。 關於我們如何以及為何走到這一步,
在本文中,我們將介紹尚未考慮的用於處理分散式事務、佇列和資料庫的庫,這些庫可以在我們的 GitHub 儲存庫中找到(
維也納NET.Sagas
當專案轉向DDD和微服務架構時,當業務邏輯分散在不同的服務中時,就會出現一個需要實現分散式事務機制的問題,因為許多場景往往會同時影響多個領域。 您可以更詳細地了解此類機制,例如,
在我們的專案中,我們實現了一個簡單但有用的機制:傳奇,或者更確切地說是基於編排的傳奇。 其本質是這樣的:有一個業務場景,需要在不同的服務中順序執行操作,如果任何一步出現問題,都需要調用之前所有步驟的回滾過程,其中假如。 因此,在傳奇結束時,無論成功與否,我們都會在所有領域收到一致的數據。
我們的實作仍然以其基本形式進行,並且不依賴與其他服務互動的任何方法的使用。 使用起來並不困難:只需建立基本抽象類別 SagaBase<T> 的後代,其中 T 是您的上下文類,您可以在其中儲存 saga 工作所需的初始資料以及一些中間結果。 上下文實例將在執行期間傳遞到所有步驟。 Saga本身是一個無狀態類,因此可以將實例作為Singleton放入DI中以獲得必要的依賴關係。
廣告範例:
public class ExampleSaga : SagaBase<ExampleContext>
{
public ExampleSaga()
{
Step("Step 1")
.WithAction(c => ...)
.WithCompensation(c => ...);
AsyncStep("Step 2")
.WithAction(async c => ...);
}
}
呼叫範例:
var saga = new ExampleSaga();
var context = new ExampleContext();
await saga.Execute(context);
維也納NET.Orm.*
一組用於透過 Nhibernate 處理各種資料庫的庫。 我們使用 Liquibase 的 DB-First 方法,因此僅具有處理現成資料庫中的資料的功能。
ViennaNET.Orm.Seedwork и ViennaNET.Orm
– 分別包含基本介面及其實作的主組件。 讓我們更詳細地看看它們的內容。
接口 IEntityFactoryService
及其實施 EntityFactoryService
是使用資料庫的主要起點,因為工作單元、用於處理特定實體的儲存庫以及命令執行器和直接 SQL 查詢都是在此處建立的。 有時,限制類別使用資料庫的功能很方便,例如,提供僅讀取資料的能力。 對於此類情況 IEntityFactoryService
有一個祖先-接口 IEntityRepositoryFactory
,它僅聲明一個用於建立儲存庫的方法。
為了直接存取資料庫,使用了提供者機制。 我們團隊中使用的每個 DBMS 都有自己的實作: ViennaNET.Orm.MSSQL, ViennaNET.Orm.Oracle, ViennaNET.Orm.SQLite, ViennaNET.Orm.PostgreSql
.
同時,多個提供者可以同時在一個應用程式中註冊,這使得例如在一個服務的框架內,無需任何修改基礎設施的成本,就可以從一個服務進行逐步遷移。一個 DBMS 到另一個 DBMS。 選擇所需連接以及特定實體類別(為其編寫映射到資料庫表)的提供者的機制是透過在 BoundedContext 類別(包含用於註冊域實體的方法)或其後繼類別中註冊實體來實現的ApplicationContext(包含註冊應用程式實體、直接請求和命令的方法),其中配置中的連線標識符被接受為參數:
"db": [
{
"nick": "mssql_connection",
"dbServerType": "MSSQL",
"ConnectionString": "...",
"useCallContext": true
},
{
"nick": "oracle_connection",
"dbServerType": "Oracle",
"ConnectionString": "..."
}
],
應用程式上下文範例:
internal sealed class DbContext : ApplicationContext
{
public DbContext()
{
AddEntity<SomeEntity>("mssql_connection");
AddEntity<MigratedSomeEntity>("oracle_connection");
AddEntity<AnotherEntity>("oracle_connection");
}
}
如果未指定連線 ID,則將使用名為「default」的連線。
實體到資料庫表的直接對應是使用標準 NHibernate 工具實現的。 您可以透過 xml 檔案和類別來使用描述。 為了方便在單元測試中編寫存根儲存庫,有一個庫 ViennaNET.TestUtils.Orm
.
可以找到使用 ViennaNET.Orm.* 的完整範例
ViennaNET.訊息傳遞。*
一組用於處理佇列的庫。
Для работы с очередями был выбран такой же подход, что и с различными СУБД, а именно – максимально возможный унифицированный подход с точки зрения работы с библиотекой, независимо от используемого менеджера очередей. Библиотека ViennaNET.Messaging
正是這種統一的責任者,並且 ViennaNET.Messaging.MQSeriesQueue, ViennaNET.Messaging.RabbitMQQueue и ViennaNET.Messaging.KafkaQueue
分別包含 IBM MQ、RabbitMQ 和 Kafka 的適配器實作。
使用佇列時,有兩個過程:接收訊息和發送訊息。
考慮接收。 這裡有 2 個選項:連續收聽和接收單一訊息。 要不斷監聽佇列,首先必須描述繼承自的處理器類 IMessageProcessor
,它將負責處理傳入的訊息。 接下來,它必須「連結」到特定隊列;這是透過在 IQueueReactorFactory
指示配置中的佇列標識符:
"messaging": {
"ApplicationName": "MyApplication"
},
"rabbitmq": {
"queues": [
{
"id": "myQueue",
"queuename": "lalala",
...
}
]
},
開始聆聽的範例:
_queueReactorFactory.Register<MyMessageProcessor>("myQueue");
var queueReactor = queueReactorFactory.CreateQueueReactor("myQueue");
queueReactor.StartProcessing();
然後,當服務啟動並呼叫該方法開始監聽時,指定佇列中的所有訊息都會轉到對應的處理器。
在工廠介面中接收單一訊息 IMessagingComponentFactory
有一個方法 CreateMessageReceiver
這將建立一個接收者,等待指定佇列中的消息:
using (var receiver = _messagingComponentFactory.CreateMessageReceiver<TestMessage>("myQueue"))
{
var message = receiver.Receive();
}
發送訊息 你需要使用相同的 IMessagingComponentFactory
並創建一個訊息發送者:
using (var sender = _messagingComponentFactory.CreateMessageSender<MyMessage>("myQueue"))
{
sender.SendMessage(new MyMessage { Value = ...});
}
序列化和反序列化訊息有三種現成的選項:僅文字、XML 和 JSON,但如果需要,您可以輕鬆建立自己的介面實現 IMessageSerializer и IMessageDeserializer
.
我們嘗試保留每個佇列管理器的獨特功能,例如 ViennaNET.Messaging.MQSeriesQueue
不僅可以發送文本,還可以發送字節訊息,並且 ViennaNET.Messaging.RabbitMQQueue
支援路由和即時佇列。 我們的 RabbitMQ 適配器包裝器也實作了某種 RPC:我們發送一條訊息並等待來自特殊臨時佇列的回應,該佇列僅為一個回應訊息而建立。
ViennaNET.CallContext
我們使用隊列不僅用於不同系統之間的集成,還用於同一應用程式的微服務之間的通信,例如在傳奇中。 這導致需要與訊息一起傳輸諸如用戶登入、端到端日誌記錄的請求識別碼、來源IP位址和授權資料等輔助資料。 為了實現這些資料的轉發,我們開發了一個函式庫 ViennaNET.CallContext
,它允許您儲存進入服務的請求的資料。 在這種情況下,透過佇列或透過 Http 發出請求的方式並不重要。 然後,在發送傳出請求或訊息之前,從上下文中獲取資料並將其放置在標頭中。 因此,下一個服務接收輔助資料並以相同的方式對其進行管理。
感謝您的關注,我們期待您的評論和請求!
來源: www.habr.com