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. HDU 4763 Theme Section

    题目: It's time for music! A lot of popular musicians are invited to join us in the music festival. Ea ...

  2. JavaScript八张思维导图—Date用法

    JS基本概念 JS操作符 JS基本语句 JS数组用法 Date用法 JS字符串用法 JS编程风格 JS编程实践 不知不觉做前端已经五年多了,无论是从最初的jQuery还是现在火热的Angular,Vu ...

  3. Content Provider Test过程中遇到的坑

    Content Provider(内容提供器) 一.什么是Content Provider? 直接贴官方文档简介图,笔者太懒了,而且 坑 不在这

  4. Oracle_SQL99_连接查询

    Oracle_SQL99_连接查询 交叉连接 cross join   --交叉连接 cross join --作用:产生两个表的笛卡尔积 select * from emp cross join d ...

  5. C#编写影院售票系统(A project with a higher amount of gold )(2:相关代码)

    此篇文章为项目代码,,,需要项目需求 ,思路分析与窗体效果请访问:http://www.cnblogs.com/lsy131479/p/8367304.html 项目类图: 影院类: using Sy ...

  6. asp.net -mvc框架复习(3)-控制器和动作方法的任务分析

    using System;using System.Collections.Generic;using System.Linq;using System.Web;//ASP.NET核心命名空间usin ...

  7. arcgis server 中Web墨卡托投影与WGS-84坐标的转换

    arcgis server 中Web墨卡托投影坐标与WGS-84坐标的转换 //经纬度转墨卡托 function lonlat2mercator(lonlat){ var mercator={x:0, ...

  8. Spring Cloud Zuul网关 Filter、熔断、重试、高可用的使用方式。

    时间过的很快,写springcloud(十):服务网关zuul初级篇还在半年前,现在已经是2018年了,我们继续探讨Zuul更高级的使用方式. 上篇文章主要介绍了Zuul网关使用模式,以及自动转发机制 ...

  9. iterator_category

    /* * 迭代器类型 * 1. input ierator * 2. write iterator * 3. forward iterator 在迭代器所形成的区间上进行读写操作 * 4. bidir ...

  10. 转-HTTP POST GET SOAP本质区别详解

    原文链接:HTTP POST GET SOAP本质区别详解 一 原理区别 一般在浏览器中输入网址访问资源都是通过GET方式:在FORM提交中,可以通过Method指定提交方式为GET或者POST,默认 ...