http://dotnet.github.io/orleans/Documentation/Getting-Started-With-Orleans/Grains.html
Grains是 Orleans编程模型的基础单元. Grains 是构建Orleans应用程式的基石 ,这些基础单元之间是隔离开的分布式并且可持久化的. Grains 是应用程序的实体对象,就像经典的OOP(面向对象编程)一样, 一个 Grain对象利用代码逻辑进行封装实体状态 . Grains彼此间保存了引用,并可以通过公开的接口进行调用.
Orleans的目的在于大大简化构建可扩展系统和消除程序可能潜在的并发性问题
一个典型的Grains封装单个实体(例如:特殊用户、设备、会话)的行为和状态。
每个Grain类型都有他的唯一地址引用.每个grains都有一个唯一标识符在他的类中,类似Key一样。他可以是一个长整型,或者GUID,或者字符串,或者长整型+字符串的组合,或GUID+字符串的组合。
一个 grain 类实现了一个或多个grain接口, 用于实现程序的基本功能。 如果想调用一个grain,调用者必须要知道grain的唯一标识符(key),还有他实现了哪个接口,才能调用具体的方法,例如,下面这两行代码展示了以用户身份如何通过调用user profile的grain来更新地址.
var user = grainFactory.GetGrain(userEmail);
await user.UpdateAddress(newAddress);
A call to GetGrain是个本地高效的操作,通过泛型来获取目标grain的引用.
注意到了没有,根本不需要自己手动创建或者实例化grain. 我们调用这个grain来更新地址的时候,他已经实例化好了,我们只需要或者他的引用,并且调用即可. 这是Orleans编程模型的高级特性之一,我们不需要创建、实例化、删除任何grains. 我们只需要写我们的逻辑代码,无需关心grain具体的实例化等,他们会一直在内存里等待我们的调用. Orleans在后台为我们自动管理所有的资源,以便我们在需要时能直接调取grains
承载Grains的容器称之为Silo. Silo构成了由物理机或者虚拟机组合成的集群。当客户需要一个grain的时候,Orleans确保在集群的Silo里能找到一个grain的实例以供调用.如果所有Silo里都没有grain,Orleans会创建一个以供调用。这个过程称之为激活( Activation).在这个案例中 Grain Persistence, 如果grain使用了持久化特性,那么在grain激活时会自动从存储库中调用状态来初始化这个grain(存储库一般为数据库)。
一旦grain在silo中被激活,那么他将会处理来自其他grain的请求(方法调用),或者来自外部的请求(例如前端web server)。
在处理请求过程的时候,grain可能会调用其他的grain或者调用外部的服务.
. 如果一个grain停止接受请求,并且闲置了一段时间(通过配置可以更改)Orleans 会把这个grain从内存移除,设为未激活状态,以此来释放内存给其他grain用。如grain又接受到了请求,Orleans会再次激活他,但可能在另外一个Silo里,然而调用者并不会察觉到这一内部处理,调用者会感觉这个grain一直就在内存里等待调用。 A grain goes through the lifecycle from existing only as its persisted state (if it has any) in storage to being instantiated in memory to being removed from memory.
Orleans 控制所有grain的激活与移除(deactivating ),在我们编写grain时,可以假定所有grain都是处于激活状态。
grain生命周期里的关键事件顺序如下:
OnActivateAsync
OnDeactivateAsyn
,那么运行他当silo正常关闭的时候,所有的grain都会被移除,所有的等待处理的调用请求都会被被转发到其他集群的silo,根据需求激活需要的grain。如果一个silo非正常关闭,那么其他的集群的silo会检测到这一失败的事件,并且开始创建激活在失败的silo里丢失的grain,用于接手失败silo的处理。注意,检测silo失败是需要一些时间的(可通过配置改变),因此重建grain的过程需要一些时间。
grain在该块的代码执行期间是激活状态,并在移动到下一个块之前结束,块的工作包括其他grain或者外部客户端调用请求的回应和上一个块完成时的闭包定时器调用。
与块对应的基本操作单元被称之为流(turn)。
虽然orleans会并行执行多次属于不同激活状态的流,但每次激活只会执行一个流,
这样做的好处是程序员并不需要用锁或者其他线程同步的方法去保证数据完整性或者多线程潜在的冲突。
接下来我们来实现一个基本的grain