从零先导搭建基于CEFGlue的CB/S的winform项目

当Saga Process Manager中生出的ICommand咋样可以帮忙重试发送而休造成操作的重

终到最后一点了,好艰苦。坚定不移即是获胜!假若现在的Saga 伊芙(Eve)nt
Handler里是碰面发生Command,这框架在殡葬那么些Command时,要保管无可知再执行。怎么惩罚为?借使在Saga
伊芙(Eve)nt
Handler里暴发的Command的Id每趟都是初new出来的一个唯一的值,这框架就不可能清楚是Command是否与事先的再一次了,框架会当就是简单个例外之Command。这里其实发生星星点点种植做法:

  1. 框架先把Saga 伊夫nt
    Handler中发出的Command保存起来,然后逐渐发送至EQueue。发送成功一个,就去一个。直到整个发送了截止。这种做法是卓有功效的,因为那样一来,大家若发送的Command就总是由存储这多少个Command的地方失去用了,所以不会师油但是生每一次只要发送的及一个Command的ID都是见仁见智之情。不过这种设计特性不是太好,因为要是发送的Command必须使先被保存起来,然后又发送,发送完将来还要去。性能达到必然不相会太强。

2.次种植做法是,Command不存储起来,而是径直将Saga 伊芙nt
Handler中生的Command拿去发送。但这种设计要求:框架对这种假诺发送的Command的ID总是以有特定的平整来发的。那种规则而管有的CommandId首先是假如唯一的,其次是规定的。下面大家看一下上边的代码:

private string BuildCommandId(ICommand command, IDomainEvent evnt, int eventHandlerTypeCode)
{
    var key = command.GetKey();
    var commandKey = key == null ? string.Empty : key.ToString();
    var commandTypeCode = _commandTypeCodeProvider.GetTypeCode(command.GetType());
    return string.Format("{0}{1}{2}{3}", evnt.Id, commandKey, eventHandlerTypeCode, commandTypeCode);
}

面是代码是一个函数,用来构建而叫发送的Command的Id的,大家得以看到ID是出于Command的一个key+要为发送的Command的类的code+当前叫拍卖的Domain
伊芙nt的ID,以及时底Saga 伊夫nt
Handler的型的code这四独音信整合。对于跟一个Domain 伊芙(Eve)nt被同一个伊芙(Eve)nt
Handler处理,然后要暴发的Command的体系也是同的,这大家基本好通过这三个音讯构建一个唯一的CommandId了,但是有时那样还不够,因为大家或许当一个伊夫nt
Handler里构建两独档次完全一致的Command,可是她们修改的聚合根的ID不同。所以,我面才有一个commandKey的一部分。那些key默认就是是Command要修改的聚合根的ID。这样,通过如此4个音讯的咬合,我们可管无有Domain
伊芙(Eve)nt被某Saga 伊芙(Eve)nt
Handler处理多少坏,最终来的Command的ID总是确定的,不转移的。当然者的commandKey有时只考虑聚合根ID可能还不够,就算本人还一贯不遭受过这种处境,呵呵。所以自己框架设计达到,就允许开发者可以重复GetKey方法,开发者需要通晓什么日期要再写是形式。看了这里的注解该就清楚了!

好了,差不多了,该睡觉了!

自一样起头下载的凡debug和release,这一点儿单都是只有dll的,没有什么用,后来本身下载了Simple版的,就是windows
Client的,下载解压后拿release下的文件复制到了winform
bin下之release就可了.
留意,一定要对准诺CEF与CEFGlue的本,下载CEF要下载Simple的异常(就我而言,我是这般多的,其它版本的若要也可运作,也可下载)

前言

ENode是一个冲音讯之架构,使用ENode开发的系统,每个环节都是处理消息,处理终结后发出新的音信。本篇作品我惦记详细分析一下ENode框架中是怎落实任何音讯处理流程的。为了重新好之知晓我背后的流水线的讲述,我觉得要应领先行拿ENode的架构图贴出,好为我们在羁押后的辨析时,可以对照这架构图举办思想和了然。

Chromium Embedded Framework (CEF)是独因Google Chromium项目标开源Web
browser控件,帮助Windows, Linux, Mac平台。
CEFGlue是此C++写就CEF类库的C#
移植版。基于此类库,我们得很有利的以我们的winform等C/S项目被放到Chrome浏览器。
当大家以档次遭到引入了CEFGlue类库之后,大家还得引入CEF类库,因为大家要经过P/Invoke来调用CEF类库的C++方法。由此要我们怀恋使以我们的C/S项目蒙引入浏览器就是用经过引入这简单只类库了。

Command的幂等处理

下边流程中之第8步,Command会被填补加至ICommandStore。这里,实际上自己加加至ICommandStore的是一个HandleCommand对象,该对象涵盖当前之Command之外,还有当前深受改动的聚合根ID。这样做的理由请看自己后的说。我们解ICommandStore会对CommandId作为主键,这样我们即便能够绝对保证一个Command不会见叫另行添加。如若Command添加到ICommandStore成功,这本来但是好了,直接进入后续之手续即可;不过如果出现CommandId重复的时刻,我们需要开哪些的处理为?

倘起还,则需要遵照CommandId(主键),把在此之前曾经持久化过之HandledCommand取出来;然后然后大家打HandledCommand拿到让改的聚合根ID,然后最根本之同一步:大家以该聚合根ID以及CommandId作为标准由I伊芙ntStore中询问有一个恐怕在的伊芙ntStream。即便有,就声明这多少个Command所发的Domain
伊芙(Eve)nt已经让持久化了,所以我们要复做相同布满发表事件之操作即可。即调用I伊芙(Eve)ntPublisher.Publish方法来发布事件到Query
Side。那么为何而披露也?因为尽管事件让持久化了,但连无意味曾经打响让颁发出去了。因为理论及闹或Domain
伊夫nt被持久化成功了,不过以如颁发事件的时候,断电了!所以这种情状下,重开服务器即相会冒出那里琢磨的情了。所以我们得再行Publish事件。

接下来,假如没有冲CommandId和聚合根ID查找到伊夫(Eve)ntStream呢?这也吓惩治,因为这种状态便证实这一个Command尽管被持久化了,可是它所来的伊芙ntStream却从不于持久化到伊夫(Eve)ntStore,所以我们用拿目前的伊芙ntStream调用I伊夫(Eve)nt瑟维斯(Service)(Service).Commit方法开展持久化事件。

其余,这里实在暴发一个疑难,为啥查找伊夫ntStream不克独依照CommandId呢?原因是:从技术上来说,我们可以仅依据CommandId来查找到一个唯一的伊芙ntStream,但诸如此类设计吧,就要求伊夫ntStore必须要援助通过一个CommandId来全局唯一定位及一个伊夫ntStream了。可是盖考虑到伊夫(Eve)ntStore的数据量是老大相当的,我们下可能相会按照聚合根ID做水平拆分(sharding)。这样的话,咱们只靠CommandId就无法精晓到什么人分片下去查找对应的伊芙ntStream了。所以,就算查询时,能以指定聚合根ID,这我们不怕可以轻轻松松知道首先到哪个分片下去找伊芙(Eve)ntStream,然后再因CommandId就会轻松定位及一个唯一的伊芙ntStream了。

既说交这边,我再说一下CommandStore的程度划分的筹划吧,CommandStore的数据量也是坏坏之,因为其会师储存所有的Command。可是幸而,大家对此CommandStore只待基于CommandId去搜寻即可,所以我们好遵照CommandId来举办Hash取模的点子来水平拆分。这样便是分片了,我们只要精晓了一个加的CommandId,也可以明白它近期凡于哪个分片下之,就不行爱找到该Command了。

所以,通过者的分析,我们领略了CommandStore和伊夫(Eve)ntStore在计划上不但考虑了争存储数据,还考虑了以后运气据量时怎么着分片,以及咋样在分片的情事下还是可以利之追寻到我们的多寡。

最终,下面还有同种植情状并未表达,就是当现身Command添加到CommandStore时发现更,可是尝试从CommandStore中冲CommandId查询该Command时,发现查不至,天呐!这种意况实际上不应当现身,倘诺出现,这表明CommandStore内部暴发问题了。因为为何添加时说爆发更,而查询也差不多来吗?呵呵。这种气象就不可能处理了,我们只好记录错误日志,然后进行持续的排查。

一定倘若注意CEFGlue和CEF的版本的相应,要不然会时有暴发问题。
说不上就是是CEF版本的下载了:
图片 1

咋样保证一个IDomain伊夫(Eve)nt只会合让一个I伊夫(Eve)ntHandler处理同涂鸦

即无异条的原故,我思大家都可以明白。比如一个伊芙nt
Handler是翻新读库的,可能大家会履行同一长条发出副成效的SQL,比如update product
set price = price + 1 where id =
1000。这长长的SQL假设为再一次执行同样次于,这price字段的价就多矣1了,这不是大家所盼的结果。所以框架需要出主题的责任可着力避免这种情景的来。这怎么落实呢?思路为是用同摆设表,记录受实践之Domain伊夫nt的ID以及当前拍卖是Domain伊夫nt的伊芙nt
Handler的项目标一个Code,对当下点儿独一起字段做并唯一索引。每便当一个伊夫nt
Handler要处理一个Domain
伊夫nt时,先判断是否已处理了,即使无处理了,则处理,处理过未来把让处理的Domain
伊夫(Eve)nt ID和伊夫ntHandler Type
Code添加到这表里即可。那要添加的时失败了也?因为有或为会面发生出现的状,导致伊夫(Eve)nt
Handler重复处理以及一个Domain
伊夫(Eve)nt。这种状态框架就不举办严酷的拍卖了,因为框架本身为不知所可成功。因为框架式不可以知道伊夫nt
Handler里面到底在开什么的。有或是殡葬邮件,也有或是记录日志,也可能是翻新读取(Read
DB)。所以,最根本之,仍然讲求伊芙nt
Handler内部,也即便是出这温馨用考虑幂等的兑现。当然框架会供被开发者必要的信来扶持他们做到严俊幂等控制。比如框架会供当前Domain
伊芙nt 的版本号被伊芙(Eve)nt Handler,这样伊芙(Eve)nt Handler里就能于Update
SQL的Where部分把这Version带齐,从而实现乐观并作控制。比如下边的代码示例:

public void Handle(IEventContext context, SectionNameChangedEvent evnt)
{
    TryUpdateRecord(connection =>
    {
        return connection.Update(
            new
            {
                Name = evnt.Name,
                UpdatedOn = evnt.Timestamp,
                Version = evnt.Version
            },
            new
            {
                Id = evnt.AggregateRootId,
                Version = evnt.Version - 1
            }, Constants.SectionTable);
    });
}

上边的代码中,当大家改进一个论坛的版块时,我们得以sql的where条件中,用version
= evnt.Verion –
1这样的标准。从而确保当前公假如处理的波一定是达到同一不行已处理的轩然大波之版号的产一个版本号,也虽然是保险了Query
Side的翻新的各种严谨和事件来的逐条一致。那样虽框架在有漏网的鱼的时,伊夫nt
Handler内部也能召开严苛的顺序控制。当然要是你的伊芙nt
Handler是发送邮件,这我还真不知道该怎么进一步保证这严格的一一或者出现争执了,呵呵。有趣味的仇人可以与自交换。

大致就是介绍至这里了。一起始,大家单位的经纪给自己错过珍重一个早就部分项目,并尝试优化。该档即是一个单机版的施用,由于客户之内需(客户之堆栈相比偏远,连不上网,由此,只可以用C/S来开,但是界面winform的界面又丑,固然就此wpf,又耗内存,而且部门并未回WPF的,假如采取GDI+以及重绘控件,又麻烦,当时她们赶时间,由此尽管起矣这一个路-基于Chrome内核的CB/S项目)。主任为我去了然下,项目代码都深受本人了,一起初即是懵逼啊,尽管从前打了webbrowes控件,但这是遵照IE,而且是既封装好之,引用下就举办,不麻烦,那会得以新的物,依然不曾放了的,于是,就想着和谐来试试看吧,找材料,然后自己长个demo,跑起,这样的话,领会会重充足。说干就干,一中断搜狗和百度,找到了片材料,如下:
基于.net开发chrome主题浏览器【二】
按照QT的webkit与ExtJs开发CB/S结构的公司应用管理网 
Xilium.CefGlue与CEF库的本匹配关系 

什么保管事件来的一一及于消费之各样相同

为什么要力保这一个相同之相继,在上头的流程手续介绍里都注解了。这里大家解析一下哪实现这些顺序的平。基本的思绪是为此一个表,存放有聚合根当前都处理过的最酷本子号,假若当前一度处理过的极特别版本号是10,这接下只可以处理这聚合根版本号为11之伊芙ntStream。尽管Version=12依旧再次后边的先期过来,也不得不等在。这怎么当为?也是类似Command的重试队列一样,在一个本土的内存队列等便执行了。比如现在极其特别已处理的版本号是10,然后现在12,13应声点儿个版号的伊芙ntStream先过来,这即便先到行列等正,然后版本号是11之这波平复了,就得拍卖。处理好下,当前最酷就处理的版本号就编程11了,所以待队列中之版本号为12底伊夫(Eve)ntStream就足以允许被拍卖了。整个控制逻辑就是是这般。那么就是单机的算法,假若集群为?实际上那不要考虑集群的图景,因为我们每台机器上且是那一个顺序控制逻辑,所以只如果集群,这最多或者现身的情事(实际上那种情状在的可能性为是生的亚)是,版本号为11的伊芙(Eve)ntStream被出现的拍卖。这种情景即便是本身下边要分析的。

此地其实还有一个细节我还并未说及,这个细节及EQueue的Consumer的ConsumerGroup相关,就是只要同样栽音讯,有成百上千Consumer消费,然后这多少个Consumer如果分为五个ConsumerGroup,这就片个ConsumerGroup的花费是相互隔离的。也就是说,所有这么些音讯,五只ConsumerGroup内的Consumer都会见花费到。这里要未做一些任何的设计,可能会晤在用户使用时遇见潜在的题目。这里我从未办法说的好掌握,说之尽领会猜度会吃大家想更烂,且因那点未是生死攸关。所以就是无举办了。有趣味之对象可看一下ENode受的伊芙(Eve)ntPublishInfo表中的伊芙ntProcessorName字段的用意。

依照CEF,用.net包装了之Xilium.CefGlue/3,基于这框架可以万分有益在公的winform等C/S项目蒙追加建筑一个内建的浏览器

Domain 伊夫nt持久化时之起争辨检测和处理

位置流程中之第10步,咱们涉:假如碰着伊夫ntStream持久化到IEventStore时遇上版本号重复(同一个聚合根ID+聚合根的Version相同,则当有起争辨),此时框架需要举办不同的逻辑处理。具体是:

第一,我们可以先想想为啥会起同一个聚合根会以几乎一模一样时刻暴发两单本子号同样的天地事件,并持久化到伊夫(Eve)ntStore。首先,我事先说一下这种景色几乎不谋面出现的理:ENode中,在ICommandExecutor在处理一个Command时,会检讨时欠Command所设修改的聚合根是否早已发最少一个聚合根正在被拍卖,假设有,则会用如今Command排入到是聚合根所对应之待队列。也就是说,它小未会师受实践。然后当当前聚合根的前方的Command被实施了了后才会自这等候队列取出下一个待的Command举办处理。通过如此的计划,大家保证了,对一个聚合根的拥有Command,不会合互相被执行,只会晤按顺序为实施。因为每个ICommandExecutor会在用的时刻,为某个聚合根自动创造那种等待队列,只要针对拖欠聚合根的Command同一时刻进来2独或上述。

这就是说,如若集群的当儿也?你同令机械的话,通过地方的措施可确保一个聚合根实例的有的Command会被依次处理。可是集群的下,可能一个聚合根会当差不多光机械被以处理了。要解决这一个题材的思绪就是对准Command遵照聚合根ID举办路由于了,因为相似只如若改聚合根的Command总是会包含一个聚合根ID,所以大家可按那特点,对深受发送的Command按照聚合根ID举办路由。只要CommandId相同,则连续会让路由于至跟一个队列,然后坐一个连串总是独自相会吃同宝机械消费,从而我们可以确保对同一个聚合根的Command总是会沾至均等贵机械及受处理。那么你或会面说,如果热数据为?比如小聚合根突然对他改的Command可能大多(扩充了同一倍增),而略则很少,这怎么处置吧?没关系,我们还有音信队列的监督平台。当出现有聚合根的Command突然坏多之时段,我们可凭EQueue的Topic的Queue可以随时开展多的风味来敷衍之题材。比如原先这Topic下才来4个Queue,这本长及8个,然后消费者机器也从4雅扩大至8玉。那样异常给Command的拍卖能力呢长了同样倍增。从而得以一本万利之解决热点数据问题。由此,这为是自个儿思要协调实现分布式信息队列EQueue的原由啊!有些场景,即使好没主意了掌控,会充裕低落,直接导致整个架构的要紧缺陷,最终导致系统瘫痪,而团结也无能为了。当然你得说咱俩可拔取Kafka,
Rocketmq那样的过人性能分布式队列,确实。然而毕竟这种巨大上之行非凡复杂,且都是非.NET平台。除了问题,维护起来自然比较自己开支之只要麻烦保障。当然除非你针对它异常会且暴发自信之运维能力。

经下边的思路实现的,确保聚合根的Command总是给顺序线性处理的设计,对伊夫ntStore有相当非常的意思。因为如此可以给伊夫ntStore不会合油不过生并发争辨,从而不会晤招无谓的对伊夫ntStore的拜访,也可大幅度的大跌伊夫ntStore的下压力。

只是什么日期或可能会晤产出并发争持吧?因为:

1)当处理Command的某台机器挂了,然后立刻尊机器所花之Queue里的音就会叫其它机器就消费。其他机器可能谋面自夫Queue里批量拉取一些Command消息来消费。然后此时使我们还开了顿时台生问题之服务器,重开完后,因为与此同时晤面开消费之Queue。然后一个要的点是,每一回一样大机械开动时,会自EQueue的Broker拉取那么些Queue最后一个于消费的音讯之职,也即使是Offset,而由于斯Offset的更新是异步的,比如5s才会更新至EQueue的Broker,所以导致这令又开后底服务器从Broker上拉取到之花费地点其实是发出延期的,从而就可能会晤失掉消费于这台此前接替你的服务器就花了之抑在消费之Command音讯了。当然这种情景为口径太苛刻,所以基本未会面时有发生,虽然会时有爆发,一般也未相会造成Command的面世执行。可是就总为是一致栽可能。实际上这里不光是某服务器挂掉后还又开的状态会促成出现争论,只如若拍卖Comand的机械的集结众多中有另的机的多或减,由于都会见招Command音信的客集群又负载均衡。在斯负载均衡的长河中,就会导致同一个Topic下之跟一个Queue里的有些消息可能相会在片雅服务器上于消费。原因是Queue的费地方(offset)的革新不是实时的,而是定时的。所以,我们一般指出,尽量不要以信很多的上做消费者集群内机器的反,而是尽量以没什么信息之时段,比如凌晨4点不时,做集群的扩容操作。这样好尽量避免所有可能带来的音讯还消费或出现顶牛之可能。呵呵,这段话或很多丁拘禁的云里雾里,我只得说交者程度了,也许如果全通晓,大家还欲针对EQueue的宏图很了然才实施!

2)固然同一个机械内,其实也是生或出现对同一个聚合根的产出修改,也不怕是对准同一个聚合根的少数单Command被同时履行。原因是:当一个Command所对应之伊芙(Eve)ntStream在给持久化时出现更,然后自己虽会放在一个地点的内存队列举办重试,然后重试由于是在另一个特意的重试线程里,该线程不是健康处理Command的线程。所以要针对拖欠聚合根后续还有Command要受拍卖,这就生或会面现出平日刻,一个聚合根被简单独Command修改的情了。

现今,大家当回到谈论,假设遇到争执时,要怎么开?这么些方面我简单关联了,就是待重试Command。但为无是如此简单的逻辑。大家得:

a.
先反省时之伊芙ntStream的Version是否为1,假若为1,表明有一个创建聚合根的Command被冒出执行了。此时大家决不在重试了,因为就还重试,这最终有的伊夫ntStream的版本号为总是1,因为假使是第一不成创聚合根,这这多少个聚合根所来的Domain伊夫nt的本子总是1。所以那种气象下,大家一味待一向打伊夫(Eve)ntStore拿出之都存在的伊夫(Eve)ntStream,然后通过I伊夫(Eve)ntPublisher.Publish方法发表该伊芙ntStream即可。为何而又发布,下边讲Command的幂等时,也说了原由,这里是一律的原委。那里呢出一个微的点要留意,就是设尝试从伊芙ntStore拿出这伊夫ntStream时,即便没有获得到呢?这些题目实际上不应出现,原因就是像面分析Command幂等经常同样,为何会冒出增长时提醒有,但询问时也翻无交之动静吧?这种情景即使是伊夫ntStore的计划来问题了,读写有非强一致性的景色了。

b.
如若手上底伊夫(Eve)ntStream的Version大于1,则大家得事先更新内存缓存(Redis),然后开Command的重试处理。为何要优先更新缓存呢?因为只要非改进,有或重试时,得到的聚合根的状态依然原有的,所以重试后仍旧促成本号争论。这为啥从缓存中获得的聚合根的状态恐怕仍旧老的吧?因为伊夫ntStream已经是于伊夫(Eve)ntStore并无意味这一个伊夫ntStream的修改都更新至缓存了。因为咱们是先期持久化到伊夫(Eve)ntStore,在更新缓存的。完全有或你还一贯不来得及更新缓存的时候,另一个Command正好用重试呢!所以,最保险的做法,就是重新重试的早晚用缓存中之聚合根状态更新到新型值。那怎么立异也?呵呵,很简单,就是经过波起点(即伊芙nt
Sourcing技术)了。我们只要打伊芙nt Store获取当前聚合根的备的伊芙(Eve)nt
Stream,然后溯源这一个事件,最后就能取聚合根的新型版本的状态了,然后更新至缓存即可。

末段,假如需要重试的语,要怎么重试呢?很粗略,只要扔到一个当地的基于内存的重试队列即可。我现凡为此BlockingCollection的。

发生矣那些基础,不再对CEF两双眼抹黑了,当然我无错过探究基础实现。于是自己便起始协调搭建了,哎,说起来都是眼泪啊,满屏的英文材料,看之眼睛疼,然则依然熬过来了,上面介绍下吧,首先给点儿个官网链接,分别是CEFGlue和CEF的下载路径:
https://bitbucket.org/xilium/xilium.cefglue/downloads/
http://opensource.spotify.com/cefbuilds/index.html
切切实实怎么用,请参考:
用CEF(CEFGLUE)作为你的客户端UI(一) 

ENode框架内部贯彻流程分析

  1. Controller发送ICommand到新闻队列(EQueue);
  2. 【从这同步最先拍卖Command】ENode.EQueue中之CommandConsumer接收至拖欠ICommand,先创制一个ICommandContext实例,然后调用ENode中的ICommandExecutor执行时ICommand并以ICommandContext传递给ICommandExecutor;
  3. ICommandExecutor按照如今ICommand的种,获取到一个唯一的ICommandHandler,然后调用ICommandHandler的Handle方法处理时ICommand,调用时传递当前的ICommandContext给ICommandHandler;
  4. ICommandHandler处理完毕Command后,ICommandExecutor获取当前ICommandContext中新增或改动的聚合根;
  5. 反省时ICommandContext中是不是就生一个骤增或改动的聚合根;如若跨越1独,则报错,通过如此的自我批评来起框架级别保证一个Command一不行只可以修改一个聚合根;
  6. 倘发现脚下猛增或改动的聚合根为0只,则直看当前底ICommand已处理完了,就调用ICommandContext的OnCommandExecuted方法,该法中会通报EQueue发送CommandResult音讯让Controller;然后Controller这边的历程,会出一个CommandResultProcessor接收到这一个CommandResult的音信,然后就清楚该ICommand的处理结果了;
  7. ICommandExecutor从ICommandContext得到眼前唯一修改的聚合根后,取出该聚合根里发出的IDomain伊夫(Eve)nt。由于一个聚合根一不善或相会平生多单IDomain伊夫(Eve)nt,所以我们会构建一个伊芙(Eve)ntStream对象。这一个指标涵盖了独具当前聚合根所出的IDomain伊夫(Eve)nt。一个伊夫(Eve)ntStream会包含众多要的音信,包括近年来ICommand的ID、聚合根的ID、聚合根的Version(版本号),以及独具的IDomain伊芙nt,等等;
  8. ICommandExecutor将该Command添加到ICommandStore。因为ICommandStore是以CommandId为主键(即Key),故倘诺CommandId重复,框架就会师通晓,然后便会开重新时之逻辑处理,这一点后又详细分析;
  9. 若果Command成功上加至ICommandStore,则属下调用I伊芙ntService的Commit方法以手上伊夫(Eve)ntStream持久化到I伊芙ntStore;
  10. I伊芙(Eve)ntService(Service)内部紧要做3起事情:1)将伊夫ntStream持久化到I伊夫ntStore;2)持久化成功后调用IMemoryCache更新缓存(缓存可以配备也地面缓存也可安排也分布式缓存Redis,尽管Command的拍卖是集群处理的,这我们应有据此共享缓存,也即便是因而Redis那种分布式缓存);3)缓存更新好之后,调用I伊夫ntPublisher接口的Publish方法将伊夫(Eve)ntStream发布出来,I伊夫(Eve)ntPublisher的现实贯彻啊会合拿当下底伊芙ntStream发送至EQueue。这3步是健康情状的流程。使赶上持久化到I伊芙ntStore时境遇版本号重复(同一个聚合根ID+聚合根的Version相同,则觉得爆发起顶牛),此时框架需要开不同的逻辑处理;这点吧于后头详细分析。
  11. 【从这无异于步开始拍卖Domain
    伊芙(Eve)nt】伊芙ntStream被ENode.EQueue中之伊芙(Eve)ntConsumer接收到,然后伊芙(Eve)ntConsumer调用I伊芙(Eve)ntProcessor处理时之伊夫ntStream;
  12. I伊夫(Eve)ntProcessor首先判断当前的伊芙ntStream是否好叫处理,这里大家得确保的良要紧的少数是,必须保证事件之持久化顺序和叫事件的订阅者处理的顺序要从严平等,否则便会起Command端的数码和Query端的Read
    DB中的数额不同等的场所。关于怎么样管那顺序的一样,后边我们于详细分析。此地先举个大概的例证来证实为什么而逐项一致。比如使现在暴发一个聚合根的一个性质,该属性之默认值是0,然后该属性先后来了三单Domain
    伊芙(Eve)nt(代表的意思分别是对准是特性做+1,*2,-1)。这两只事件而仍这样的各类来后,这这特性最终之价是1;可是只要及时3单事件被消费者花之次第是+1,-1,*2这末了之结果就非是1了,而是0了;所以经此例子,我怀想我们应该都知道了怎么要从严管教聚合根持久化事件之次第必须同叫消费的顺序要完全一致了;
  13. 一旦当前之伊夫(Eve)ntStream允许给处理,则I伊芙(Eve)ntProcessor对近日底伊夫ntStream中之每个IDomain伊夫nt做如下处理:1)遵照IDomain伊芙nt的种得到所有当前I伊夫(Eve)ntProcessor节点上存有注册的I伊芙ntHandler,然后调用它们的Handle方法,完成本Query端的Read
    DB的换代。然工作还未曾这粗略,因为大家尚需要保证当前底IDomain伊夫(Eve)nt只会师叫当下的I伊芙(Eve)ntHandler处理同次于否则I伊芙ntHandler就谋面因为还处理了IDomain伊芙(Eve)nt而造成最后之多少错乱;这里的幂等也当前边详细谈论。
  14. 些微I伊夫(Eve)ntHandler处理完IDomain伊芙nt后会出新的ICommand(就是Saga
    Process
    Manager)的图景。这种情景下,大家尚待把这个有的ICommand由框架自动发送到信息队列(EQueue);不过事情也远非这简单,假使发送这多少个ICommand失利了邪?这就是说虽然需要重发,这重发怎样筹划才能保证不管重发多少次,也未会合促成ICommand的双重执可以吗?这里实在最好着重之一点凡是使保管你每一回重发的ICommand的Id总是跟第一糟发送时假如一致的,否则框架就无法理解是否是跟一个Command了。这里的切实统筹后更分析。

ENode架构图

图片 2

发表评论

电子邮件地址不会被公开。 必填项已用*标注