標籤:
ENode架構初始化前言
Conference案例是使用ENode架構來開發的。之前我沒有介紹過ENode架構是如何啟動的,以及啟動時要注意的一些點,估計很多人對ENode架構的初始化這一塊感覺很複雜,一頭霧水。所以,本文想簡單介紹一下在做一個實際項目時,我們該如何初始化ENode。
使用ENode開發的項目的頂層宿主工程一般有兩類:1)前台Web項目,它的職責就是發送命令;2)後台ProcessorHost項目,負責處理命令或事件;
這兩類項目的初始化方式完全一樣,只是Web項目可能需要多初始化Controller的容器管理。下面我們看看使用ENode架構時的主要初始化邏輯:
private static void InitializeECommon(){ _ecommonConfiguration = ECommonConfiguration .Create() .UseAutofac() .RegisterCommonComponents() .UseLog4Net() .UseJsonNet() .RegisterUnhandledExceptionHandler(); _logger = ObjectContainer.Resolve<ILoggerFactory>().Create(typeof(Bootstrap).FullName); _logger.Info("ECommon initialized.");}private static void InitializeENode(){ ConfigSettings.Initialize(); var assemblies = new[] { Assembly.Load("Conference.Common"), Assembly.Load("Registration.Domain"), Assembly.Load("Registration.CommandHandlers"), Assembly.Load("Registration.ProcessManagers"), Assembly.Load("Registration.ReadModel"), Assembly.Load("Registration.ProcessorHost") }; var setting = new ConfigurationSetting { SqlServerDefaultConnectionString = ConfigSettings.ConferenceENodeConnectionString }; _enodeConfiguration = _ecommonConfiguration .CreateENode(setting) .RegisterENodeComponents() .RegisterBusinessComponents(assemblies) .RegisterAllTypeCodes() .UseSqlServerLockService() .UseSqlServerCommandStore() .UseSqlServerEventStore() .UseSqlServerSequenceMessagePublishedVersionStore() .UseSqlServerMessageHandleRecordStore() .UseEQueue() .InitializeBusinessAssemblies(assemblies); _logger.Info("ENode initialized.");}
從上面的代碼可以看到,ENode架構的初始化是採用FluentAPI的方式,通過使用富有語義的方法來表達當前在做什麼配置。
ECommon初始化
配置ENode一般是分為兩個階段,先配置ECommon。ECommon的初始化邏輯比較簡單,Create方法建立一個ECommonConfiguration類的執行個體,然後通過調用UseAutofac方法,告訴架構當前使用的是Autofac容器。 然後RegisterCommonComponents就是把一些預設的組件注入到容器中;然後UseLog4Net和UseJsonNet這兩個方法就是告訴架構當前使用的日誌組件是Log4Net,JSON序列化組件是JSON.NET。最後,RegisterUnhandledExceptionHandler方法就是告訴架構要捕獲未處理的異常,這樣架構在發現有未處理的異常時,會嘗試記錄錯誤記錄檔。上面的一個配置的兩點是,ENode並沒有和特定的容器綁定,目前僅實現了Autofac,大家可以根據自己的喜好使用其他的IoC容器,比如Untity, Castle, StructureMap等。
ENode初始化
ECommon初始化完成後,開始初始化ENode架構了。ENode的初始化稍微複雜一點,但因為也是通過富有語義的FluentAPI來實現初始化,所以看起來也比較容易理解。
基本配置
我們先建立ENodeConfiguration的全域執行個體,然後同樣註冊ENode的所有預設實現組件以及給定程式集中的所有標記了Component特性的組件到容器。
類型和Code的映射配置
然後接下來就是通過RegisterAllTypeCodes方法告訴架構所有可能涉及到序列化的類的Code。為什麼要做這個配置?因為假如一個Command要發送到訊息佇列,在Command發送時,會用JSON序列化;在Command消費者消費時會用JSON還原序列化。如果我們不設計這種Code機制,那序列化的JSON字串裡需要包含類型的名稱。而我們的類名可能會調整的。所以為了更好的靈活性,我為ENode架構所有可能需要序列化的地方,都通過這種Code的思想,把類型轉換為Code;還原序列化時,根據Code找到對應的類型,然後進行還原序列化;該方法的內部實現大概如下面這樣:
public static ENodeConfiguration RegisterAllTypeCodes(this ENodeConfiguration enodeConfiguration){ var provider = ObjectContainer.Resolve<ITypeCodeProvider>() as DefaultTypeCodeProvider; //aggregates provider.RegisterType<Order>(120); provider.RegisterType<OrderSeatAssignments>(121); //commands provider.RegisterType<MakeSeatReservation>(207); provider.RegisterType<CommitSeatReservation>(208); provider.RegisterType<CancelSeatReservation>(209);持久化相關實作類別的配置
由於真實的項目,我們肯定需要做各種持久化,比如事件的持久化,事件處理記錄的持久化等。所以通過上面代碼中的各種UseSql打頭的方法,就可以配置各種情境的持久化實現。然後這些Sql的實作類別所使用的資料庫連接,上面的例子都是使用預設的資料庫連接,見上面SqlServerDefaultConnectionString屬性的賦值。如果你只是想運行一下基於記憶體的模型,那這些UseSql的方法是不需要調用的。比如像ENode架構裡內建的一些例子的配置都很簡單,以NoteSample為例:
static void InitializeENodeFramework(){ var assemblies = new[] { Assembly.Load("NoteSample.Domain"), Assembly.Load("NoteSample.Commands"), Assembly.Load("NoteSample.CommandHandlers"), Assembly.GetExecutingAssembly() }; _configuration = Configuration .Create() .UseAutofac() .RegisterCommonComponents() .UseLog4Net() .UseJsonNet() .RegisterUnhandledExceptionHandler() .CreateENode() .RegisterENodeComponents() .RegisterBusinessComponents(assemblies) .RegisterAllTypeCodes() .UseEQueue() .InitializeBusinessAssemblies(assemblies) .StartEQueue(); Console.WriteLine(string.Empty); _logger = ObjectContainer.Resolve<ILoggerFactory>().Create(typeof(Program).Name); _logger.Info("ENode started...");}EQueue相關配置
因為ENode需要使用EQueue這個分布式訊息佇列來實現命令或事件的發布和訂閱。所以,我們也需要配置EQueue。通過調用UseEQueue實現,該方法的內部實現需要根據當前伺服器所需要使用的功能而定。
類似於像下面這樣:
public static ENodeConfiguration UseEQueue(this ENodeConfiguration enodeConfiguration){ var configuration = enodeConfiguration.GetCommonConfiguration(); configuration.RegisterEQueueComponents(); var producerEndpoint = new IPEndPoint(SocketUtils.GetLocalIPV4(), ConfigSettings.BrokerProducerPort); var consumerEndpoint = new IPEndPoint(SocketUtils.GetLocalIPV4(), ConfigSettings.BrokerConsumerPort); var producerSetting = new ProducerSetting { BrokerProducerIPEndPoint = producerEndpoint }; var consumerSetting = new ConsumerSetting { BrokerConsumerIPEndPoint = consumerEndpoint }; _applicationMessagePublisher = new ApplicationMessagePublisher("PaymentsApplicationMessagePublisher", producerSetting); _domainEventPublisher = new DomainEventPublisher("PaymentsDomainEventPublisher", producerSetting); configuration.SetDefault<IMessagePublisher<IApplicationMessage>, ApplicationMessagePublisher>(_applicationMessagePublisher); configuration.SetDefault<IMessagePublisher<DomainEventStreamMessage>, DomainEventPublisher>(_domainEventPublisher); _commandConsumer = new CommandConsumer( "PaymentCommandConsumer", "PaymentCommandConsumerGroup", consumerSetting) .Subscribe(Topics.PaymentCommandTopic); _eventConsumer = new DomainEventConsumer( "PaymentEventConsumer", "PaymentEventConsumerGroup", consumerSetting) .Subscribe(Topics.PaymentDomainEventTopic); return enodeConfiguration;}
使用ENode架構開發的應用,可能會用到如下的角色:
- 發送命令,使用CommandService;
- 發送領域事件,使用DomainEventPublisher;
- 發送應用程式層訊息,使用ApplicationMessagePublisher;
- 發送異常,使用PublishableExceptionPublisher;
- 消費命令,使用CommandConsumer;
- 消費領域事件,DomainEventConsumer;
- 消費應用程式層訊息,使用ApplicationMessageConsumer;
- 消費異常,使用PublishableExceptionConsumer;
從上面的8個角色中,我們可以知道,主要分為兩類:1)訊息生產者;2)訊息消費者;前面4個是訊息生產者;後面4個屬於訊息消費者;然後生產者內部就是封裝了EQueue的Producer;消費者內部就是封裝了EQueue的Consumer;是不是很對稱,呵呵。
然後一般一個Web工程,它只需要發送命令即可,所以一般只需要初始化CommandService即可;而一般一個ProcessorHost工程,因為可能要處理命令、事件、應用程式層訊息,以及異常訊息。且因為可能有Saga流程的存在,所以它們可能需要配置所有上面8個角色。具體需要配置哪些角色,還是要看具體的應用而定。
當我們執行個體化好了需要的角色後,我們就可以啟動或者關閉它們了。代碼類似如下:
public static ENodeConfiguration StartEQueue(this ENodeConfiguration enodeConfiguration){ _applicationMessageConsumer.Start(); _eventConsumer.Start(); _commandConsumer.Start(); _domainEventPublisher.Start(); _commandService.Start(); return enodeConfiguration;}public static ENodeConfiguration ShutdownEQueue(this ENodeConfiguration enodeConfiguration){ _commandService.Shutdown(); _domainEventPublisher.Shutdown(); _commandConsumer.Shutdown(); _eventConsumer.Shutdown(); _applicationMessageConsumer.Shutdown(); return enodeConfiguration;}關於Controller所依賴的服務如何注入
上面介紹了ENode架構的主要配置如何使用,但還有一種情境沒有介紹。就是ASP.NET MVC Web項目,Controller往往也是需要讓容器來管理執行個體的。那這個如何配置呢?其實也很簡單。我們只需要在ENode架構配置完成後,在通過如下的代碼就可以實現Controller的生命週期的管理了。
private void RegisterControllers(){ var webAssembly = Assembly.GetExecutingAssembly(); var container = (ObjectContainer.Current as AutofacObjectContainer).Container; var builder = new ContainerBuilder(); builder.RegisterControllers(webAssembly); builder.Update(container); DependencyResolver.SetResolver(new AutofacDependencyResolver(container));}
上面的代碼中,關注點是:AutofacDependencyResolver這個類,這個類是Autofac.Integration.Mvc這個組件提供的。這個組件是Autofac和MVC整合的一個組件。通過它,我們可以方便的實現對Controller的依賴注入。
結束語
好了,基本介紹了一下ENode架構的配置部分如何使用。是不是很複雜?呵呵,其實只要我寫的這些例子你看多了,也是很簡單的,大家做項目時參考我的案例中的寫法即可。
分類: DDD 案例分析
ENode架構初始化