Orleans之EventSourcing

这是Orleans系列文章中的一篇.首篇文章在此

引入:

如果没有意外,我再这篇文章中用ES代替EventSourcing,如果碰到"事件回溯","事件溯源","事溯"等词语,都一般代表Eventsourcing.

如果引入Orleans而不用es的话,那就只用了Orleans一半的优点,多线程编程的逻辑\排错的简化以及可分布式.下面我聊聊重头戏ES,这些都是我个人理解,如果有错误欢迎指正.

有时候产生一个理论是为了解决目前的困难,但是随着事物的发展,原先的困难不再是主要困难,新的夜王必然会出现,听闻三眼乌鸦要成为新的夜王我很是不解.马丁这么写会收到愤怒的读者寄来的刀片,我估计马丁把收到全世界的刀片都卖了,都比他的小说值钱.扯远了。总而言之,我们关注的矛盾点会随着时间变化而变化。以前为了节省存储空间(当然并不仅仅是为了空间考虑),对数据库的设计提出了三个范式,来指导数据库的设计.可是现在时代变了狗蛋,存储空间不值钱了.现在数据库读写成为了新的瓶颈,所以我们设计数据库的理念就应该变了.

ES遵循这一个简单的思想,就是存储的时候只存储变化量,而不存储最终结果.需要最终结果的地方,就必须提取所有的变化量以及初始状态,让它们相加得到最终结果.这样的看起来是个麻烦的迂回(我不就是想得到最终结果嘛),但是却隐藏着一个事实:由于只存储变化量,意味着数据只增不减,意味着数据存储后就不会被更改,意味着高并发和高吞吐量.但是有个缺点就是数据量增加很多,现在硬盘是最不值钱的,如果随着时间的推移,变化量变的很多,要得到最终结果需要大量的运算,但是.这些缺点不算什么大缺点,有方法可以避免.虽然一直说空间不值钱,并不是说数据大小无关紧要,在合理的设计中,数据应当能小则小.

简单的一句总结就是满足只增不减,存后不改的数据,都能够设计成ES.以便于提高吞吐量.

为啥只增不减会提供吞吐量?因为数据库的数据永远不变,所以就放心大胆的读取吧。哪怕是多线程都不需要架锁。可是这里隐藏着另一个风险,你读取的数据不一定是最新的。这个"非最新"的确是个难题,不过好消息是,如果你一直读,最终能够读到最新的。这就是"最终一致性"。

关于es网络上有很多的文章,可以拿来读读,这里就不做过多的叙述了。

Orleans内置了三种ES的实现方式,但并不是我提到的ES.它内置的三种ES实现方式,分别是:StateStorage.LogConsistencyProvider,LogStorage.LogConsistencyProvider和CustomStorage.LogConsistencyProvider。这里我只使用介绍官方例子,它使用的是CustomStorage这个类。

使用Orleans达成ES,要明白几个基本的概念,所谓的Event是个啥东西?在Orleans中Event一般是指的自定义的grain事件,这些事件的发生更改了grain的状态。笼统的讲就是更改了grain类相关的各种变量的值。那么你要把这些值存起来,就要问两个问题,存到哪里去,用哪个类来控制存。又要要求溯源,那就必须读,那就要解决用什么序列化的问题。这本质上是一个问题:用什么类控制存储。

我打算拿官方的例子来介绍一下ES在orleans中基本实现方式。看完介绍,如果读者要实现自己的ES,可以仿照更改。Orleans源码里有一个例子是ReplicatedEventSample(以下简称RES),这个是我下面要说的重点。

RES例子是一个网页例子,它的网页如何体现的,以及如何运行RES,我将不做介绍,这里我只是重点说明网页后边的支撑程序:就是以下三个项目

其中主要设计的类总体图像如下:

EventGrain是这个项目的主角,它接受到外界发送来的一些消息,把这些消息使用ES的方式存储下来。在使用RES的时候,要注意EventGrain的基类以及需要实现的接口。

EventState是Grain的状态值。

ReplicatedEventTable控制着实际的读取与存储。

GeneratorGrain一个消息制造器,它发送OutCome消息给EventGrain

TickerGrain,可有可无的东西。

它们工作流程如下

刚开始启动的时候,silo会调用GeneratorGrain,这个GeneratorGrain的n个实例会激活EventGrain并每2.5秒发送一次OutCome消息。如下图

EventGrain接受到激活指令后初始化一个ReplicatedEventTable实例用来控制自己的存储,并调用RefreshNow()获取最新的状态,这时候orleans会调用ReadStateFromStorage,EventGrain在这里使用ReplicatedEventTable完成真正的读取动作。

GeneratorGrain每隔2.5秒制造一个OutCome消息发送给EventGrain,EventGrain接受到消息并立即确认此消息,这时候可以在OnStateChanged函数中针对Grain状态值变化做一些必要的处理。这些必要的处理不应该包括ES存储动作,ES存储动作会在ApplyUpdatesToStorage中进行处理,它里面使用了ReplicatedEventTable来进行真正的存储。

这样一个简单的ES就实现了。这个ES里面的每一条数据都是OutCome,我们只需要改写ReplicatedEventTable实现自己的存储控制类。在本例子中,使用的是AzureTable作为存储媒介的。

这里有几个问题,如果我改写后的新存储控制类,假设名字是EventSql是一个普通类,在EventSql中我精确的控制了sql连接,sql读取等等。因为EventGrain是一个key一个实例的。在一个soli中有可能存在非常多个Grain实例,这时候我再使用EventSql就有可能会产生很多的链接,导致出错。要改进这里,只需要让EventSql本身也是扩展自Grain(而不仅仅是一个普通的类)。同时使用一些机制来控制链接数。可以池化这些EventSqlGrain,使用的时候从池子中取出一个。

这样官方的例子就算介绍完毕了,剩下的来聊聊一些其他细节的问题。

Orleans运行时会时不时的自动更新Grain的状态值。但是有时候也是需要针对某些事件,我们自定义的去更新状态值,要实现这样的动作,可以重写TransitionState 函数。

ReadStateFromStorage返回的值中需要有和一个State的默认值。ApplyUpdatesToStorage有时候会存储失败,这时候运行时会进行重试。有时候虽然状态值已经实际存储了,但是却返回一个错误,这样会造成同一状态值的重复存储,这里就需要程序有一个过滤机制或者确保一个重复的状态值不会对程序逻辑造成损害。

使用Orleans.EventSourcing.CustomStorage.LogConsistencyProvider(本例就是),它并不支持JournaledGrain类中的RetrieveConfirmedEvents方法,如果需要使用它,要另行实现。

这里只是我个人写一些基础的东西,具体改写还是需要自己去实现,刚开始可以仿照这个RES进行改写,把OutCome存储到自己想要的地方。等改写完毕就会一个大致的概念。

至此我写完了orleans所有的主要方面,剩余的orleans底层的实现细节,还是需要自己对照文档啃源码了。

ES是实现业务需求的方法,一个项目本身是一个客观物体,我们选择了使用ES的办法去实现它,会将这个项目的复杂度进行转移,降低了业务在程序实现上的复杂度而同时增加了数据设计的复杂度。ES这个新的方法会让某些复杂的业务逻辑变的简单,特别适合于那些需要高并发与高吞吐量的场景。

开篇曾经说过的,"如果随着时间的推移,变化量变的很多,要得到最终结果需要大量的运算"是个缺点,要克服这个缺点,可以这样搞:每隔一段时间(比如一周)就计算所有的最终结果,并把这个最终结果当作新的初始值,同时把变化量转移备份。简单的如下图所示:

Orleans之EventSourcing的更多相关文章

  1. Orleans 高级特性-目录

    这里将介绍一些Orleans的高级特性,适合对Orleans已经有不少了解的用户,先列出一个索引,博客文章慢慢补充 1.使用Immutable 优化复制 2.自定义序列化 (待完成) 3.可重入 Gr ...

  2. .NET的Actor模型:Orleans

    Orleans是微软推出的类似Scala Akka的Actor模型,Orleans是一个建立在.NET之上的,设计的目标是为了方便程序员开发需要大规模扩展的云服务, 可用于实现DDD+EventSou ...

  3. 微软分布式框架Orleans开源了

    开源地址: https://github.com/dotnet/orleans 昨天编译了一下,这个最新的Orleans安装程序(用github源码编译的) 下载地址:http://pan.baidu ...

  4. Orleans:NET的Actor模型

    .NET的Actor模型:Orleans   Orleans是微软推出的类似Scala Akka的Actor模型,Orleans是一个建立在.NET之上的,设计的目标是为了方便程序员开发需要大规模扩展 ...

  5. akka.net与微软分布式框架Orleans

    微软分布式框架Orleans开源了 开源地址: https://github.com/dotnet/orleans 昨天编译了一下,这个最新的Orleans安装程序(用github源码编译的) 下载地 ...

  6. Orleans稍微复杂的例子—互动

    这是Orleans系列文章中的一篇.首篇文章在此 我费力费心的翻译过官方的教程,但是本人英语词汇量不高,可是架不住电子词典啊-只要肯花时间,我这些内容谁都可以做出来.所以这个事例告诉我们一个道理,那就 ...

  7. Orleans—一些概念

    Orleans-一些概念 这是Orleans系列文章中的一篇.首篇文章在此 这个文章聊一聊Orleans的概念.以下文章大部分翻译自官方教程,还有一些结合实际的应用经验,并对以前文章留下的坑进行填平. ...

  8. Orleans的集群构建

    Orleans的集群构建 这是Orleans系列文章中的一篇.首篇文章在此 听闻一周前,微软公布了.net core2.0,以及各种各样的其他core2.0.大家都很兴奋.微妈的诚意真是满满的.这次开 ...

  9. Orleans实战目录

    一 项目结构 1> 接口项目 .net core类库 2> Grains实现项目 .net core类库 3> 服务Host .net core console applicatio ...

随机推荐

  1. Lucene学习笔记1(V7.1)

    Lucene是一个搜索类库,solr.nutch和elasticsearch都是基于Lucene.个人感觉学习高级搜索引擎应用程序之前 有必要了解Lucene. 开发环境:idea maven spr ...

  2. dig(域信息搜索器)命令

    dig命令   dig命令是常用的域名查询工具,可以用来测试域名系统工作是否正常. 语法 dig(选项)(参数) 选项 @<服务器地址>:指定进行域名解析的域名服务器: -b<ip地 ...

  3. element-ui中upload组件如何传递文件及其他参数

    最近项目用到了vuethink,里面集成了element-ui,之前一直用的是bootstrap框架,对js也是一知半解,然后也用过vue.js,但也是学的不通透的,然后就各种入坑. 下面就分析一下我 ...

  4. MyBatis之基于XML的动态SQL

    先说下我的梦想,大学的时候一直想着是能开店卖胡辣汤,到目前依然还是我的梦想,上周一家出版社联系我问我有没有时间可以合作出书,这也是我的梦想之一,想了想还是放弃了,至少觉得目前不行,毕竟工作还不到五年, ...

  5. CUDA共享内存的使用示例

    CUDA共享内存使用示例如下:参考教材<GPU高性能编程CUDA实战>.P54-P65 教材下载地址:http://download.csdn.net/download/yizhaoyan ...

  6. P1345 [USACO5.4]奶牛的电信Telecowmunication

    P1345 [USACO5.4]奶牛的电信Telecowmunication 题目描述 农夫约翰的奶牛们喜欢通过电邮保持联系,于是她们建立了一个奶牛电脑网络,以便互相交流.这些机器用如下的方式发送电邮 ...

  7. C语言的函数调用过程

    从汇编的角度解析函数调用过程 看看下面这个简单函数的调用过程: int Add(int x,int y) { ; sum = x + y; return sum; } int main () { ; ...

  8. linkin大话设计模式--桥接模式

    linkin大话设计模式--桥接模式 桥接模式是一种结构化模式,他主要应对的是:由于实际的需要,某个类具有2个或者2个以上维度的变化,如果只是使用继承将无法实现功能,或者会使得设计变得相当的臃肿.我们 ...

  9. MoonLight可视化订单需求区域分析系统前端

    MoonLight可视化订单需求区域分析系统实现功能: 在现实生活中,计算机和互联网迅速发展,人们越来越趋向于网络,于是我们就有了各种各样的系统,来帮助我们更好地生活.比如对于打车来说,我们也可以通过 ...

  10. MySQL中查询表及索引大小的方法

    查询MySQL表的大小及索引大小可以通过系统库information_schema中的TABLES表来实现. 该表常用的一些字段: TABLE_SCHEMA:数据库名TABLE_NAME:表名ENGI ...