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>();
provider.RegisterType<OrderSeatAssignments>(); //commands
provider.RegisterType<MakeSeatReservation>();
provider.RegisterType<CommitSeatReservation>();
provider.RegisterType<CancelSeatReservation>();
持久化相关实现类的配置
由于真实的项目,我们肯定需要做各种持久化,比如事件的持久化,事件处理记录的持久化等。所以通过上面代码中的各种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框架的配置部分如何使用。是不是很复杂?呵呵,其实只要我写的这些例子你看多了,也是很简单的,大家做项目时参考我的案例中的写法即可。
ENode框架Conference案例分析系列之 - ENode框架初始化的更多相关文章
- ENode框架Conference案例分析系列之 - 文章索引
ENode框架Conference案例分析系列之 - 业务简介 ENode框架Conference案例分析系列之 - 上下文划分和领域建模 ENode框架Conference案例分析系列之 - 架构设 ...
- ENode框架Conference案例分析系列之 - 架构设计
Conference架构概述 先贴一下Conference案例的在线地址,UI因为完全拿了微软的实现,所以都是英文的,以后我有空再改为中文的. Conference后台会议管理:http://www. ...
- ENode框架Conference案例分析系列之 - 业务简介
前言 ENode是一个应用开发框架.通过ENode,我们可以方便的开发基于DDD+CQRS+EventSourcing+EDA架构的应用程序.之前我已经写了很多关于ENode的架构以及设计原理的文章, ...
- ENode框架Conference案例分析系列之 - 复杂情况的读库更新设计
问题背景 Conference案例,是一个关于在线创建会议(类似QCon这种全球开发者大会).在线管理会议位置信息.在线预订某个会议的位置的,这样一个系统.具体可以看微软的这个项目的主页:http:/ ...
- ENode框架Conference案例分析系列之 - 订单处理减库存的设计
前言 前面的文章,我介绍了Conference案例的业务.上下文划分.领域模型.架构,以及代码整体流程.接下来想针对案例中一些重要的场景,分别做进一步的分析.本文想先介绍一下Conference案例的 ...
- ENode框架Conference案例分析系列之 - Quick Start
前言 前一篇文章介绍了Conference案例的架构设计,本篇文章开始介绍Conference案例的代码实现.由于代码比较多,一开始就全部介绍所有细节,估计很多人接受不了,也理解不了.所以,我先进行一 ...
- ENode框架Conference案例分析系列之 - 事件溯源如何处理重构问题
前言 本文可能对大多数不太了解ENode的朋友来说,理解起来比较费劲,这篇文章主要讲思路,而不是一上来就讲结果.我写文章,总是希望能把自己的思考过程尽量能表达出来,能让大家知道每一个设计背后的思考的东 ...
- ENode框架Conference案例分析系列之 - 上下文划分和领域建模
前面一片文章,我介绍了Conference案例的核心业务,为了方便后面的分析,我这里再列一下: 业务描述 Conference是这样一个系统,它提供了一个在线创建会议以及预订会议座位的平台.这个系统的 ...
- ENode框架Conference案例转载
ENode框架Conference案例分析系列之 - Quick Start 前言 前一篇文章介绍了Conference案例的架构设计,本篇文章开始介绍Conference案例的代码实现.由于代码比较 ...
随机推荐
- 弹出层提示,X秒后关闭
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <HEAD ...
- APPCAN开发笔记:html页面之间的参数传递:使用js获取url中的参数,以及在APPCAN中不能使用的解决方法
用PHP的GET/POST方式来传递方式已经是司空见惯了,但是如果我的页面是一个静态的html的页面,想传递参数的时候要怎么办呢?在APPCAN的开发中我们会经常遇到这样的问题,因为所有的页面都是静态 ...
- idapython实现动态函数调用批量注释
部门小伙伴遇到一个样本需要对动态函数调用就行批量注释还原的问题,通过idapython可以大大的减少工作量,其实这一问题也是很多样本分析中最耗时间的一块,下面来看看如何解决这个问题(好吧这才是今年最后 ...
- find out the neighbouring max D_value by counting sort in stack
#include <stdio.h> #include <malloc.h> #define MAX_STACK 10 ; // define the node of stac ...
- 如何用hypermesh生成包含interface的流体网格
在计算气动声学的时候,有些情况是需要我们提取流体计算的结果作为声学分析的边界条件,但是,有些流体网格因为物理模型的问题需要我们设定interface,恰恰你是机械,对流体了解一点,又不想花费太多时间来 ...
- 一台电脑安装多个版本的jdk
我们平时在做Java开发的时候,有时需要使用多个版本的jdk, 那么一台电脑上需要安装多个JDK了. 那一台电脑上可不可以同时安装多个版本的jdk呢? 答案是可以的! 但使用的时候,只能使用一个,不能 ...
- topcoder SRM 628 DIV2 BracketExpressions
先用dfs搜索所有的情况,然后判断每种情况是不是括号匹配 #include <vector> #include <string> #include <list> # ...
- svn客户端重新设置用户名和密码
在第一次使用TortoiseSVN从服务器CheckOut的时候,会要求输入用户名和密码,这时输入框下面有个选项是保存认证信息,如果选了这个选项,那么以后就不用每次都输入一遍用户名密码了. 不过,如果 ...
- HDU 2509 Nim博弈变形
1.HDU 2509 2.题意:n堆苹果,两个人轮流,每次从一堆中取连续的多个,至少取一个,最后取光者败. 3.总结:Nim博弈的变形,还是不知道怎么分析,,,,看了大牛的博客. 传送门 首先给出结 ...
- Sublime Text3的安装
作为一名前端开发小白,使用Sublime两年多了,从当初的Sublime Text 2到如今的Sublime Text 3,非常喜欢这款轻量级编译器,它不像Dreamweaver那样动辄几百M,只有仅 ...