ENode框架Conference案例分析系列之 - 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框架開發的應用,可能會用到如下的角色:

  1. 發送命令,使用CommandService;
  2. 發送領域事件,使用DomainEventPublisher;
  3. 發送應用層消息,使用ApplicationMessagePublisher;
  4. 發送異常,使用PublishableExceptionPublisher;
  5. 消費命令,使用CommandConsumer;
  6. 消費領域事件,DomainEventConsumer;
  7. 消費應用層消息,使用ApplicationMessageConsumer;
  8. 消費異常,使用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框架的配置部分如何使用。是不是很復雜?呵呵,其實只要我寫的這些例子你看多了,也是很簡單的,大家做項目時參考我的案例中的寫法即可。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM