Grains是Orleans编程模型的关键原语。Grains是Orleans应用程序的基石,它们是隔离,分发和持久性的原子单元。Grains是表示应用程序实体的对象。就像在经典的面向对象编程中一样,grain封装了实体的状态,并在代码逻辑中对其行为进行编码。Grains可以通过调用彼此通过接口公开的方法,来保持彼此的引用并进行交互。

Orleans旨在大大简化构建可伸缩应用程序并消除大多数并发性挑战

  • 不通过消息传递在grains实例之间共享数据
  • 通过为每个单独的grain提供单线程执行保证。

典型的grain封装单个实体(例如特定用户或设备或会话)的状态和行为。

Grain标识

单个grain是grain类型(类)的唯一可寻址实例。每种grain在其类型中具有唯一的身份,也称为grain钥匙。其类型中的grain标识可以是长整数,GUID,字符串或long + string或GUID + string的组合。

访问一个Grain

grain类实现一个或多个grain接口,正式代码契约,用于与该类型的grain交互。要调用grain,调用者需要知道grain类实现的grain接口,其中包括调用者想要调用的方法,以及目标grain的唯一标识(key)。例如,如果将电子邮件用作用户身份,则可以使用以下方式调用用户配置文件以更新用户的地址。

var user = grainFactory.GetGrain<IUserProfile>(userEmail);
await user.UpdateAddress(newAddress);

对GetGrain的调用是一种廉价的局部操作,用于构造具有嵌入的标识,和目标grain的类型的grain引用。

请注意,无需创建或实例化目标grain。我们调用它来更新用户的地址,就像用户的grain已经为我们实例化一样。这是Orleans编程模型的最大优势之一我们永远不需要创建,实例化或删除grains。我们可以编写代码,好像所有可能的grains(例如数百万用户配置文件)始终在内存中等待我们调用它们。在幕后,Orleans运行时执行所有繁重的管理资源的工作,以便在需要时透明地将grains写入内存。

在后台 - Grain生命周期

Grains运行在称为silos的执行容器中。Silos形成一个集合了多个物理或虚拟机资源的集群。当grain有工作(请求)时,Orleans确保群集中的一个Silos上有一个grain实例。如果任何Silos上没有谷物实例,Orleans运行时会创建一个。此过程称为激活。在grain使用Grain Persistence的情况下,运行时会在激活时自动从后备存储中读取状态。

一旦在silo上激活,grain就会处理来自其他grains或来自群集外部(通常来自前端Web服务器)的传入请求(方法调用)。在处理请求的过程中,grain可能会调用其他grains或一些外部服务。如果grain停止接收请求并保持空闲,则在可配置的不活动时段后,Orleans会从内存中移除grain(停用grain)以释放其他grain的资源。如果有新的grain要求,Orleans会再次激活它,可能是在不同的silo上,所以调用者会得到这样的印象:grain一直留在内存中。grain经历的生命周期,是从仅作为存在其存储中的持久化状态(如果有的话),到在内存中实例化,在到从内存中删除。

httpfileaionlifexyzsourcedownloadid5bd705d4dc72d91ff8935cc5

Orleans透明地控制着激活和释放grains的过程。当编写grain代码时,开发者假定所有grains都被激活。

grain生命周期中的关键事件序列如下所示。

  • 另一个grain或客户调用grain的方法(通过grain引用)
  • grain被激活(如果它尚未在集群中的某个位置激活)并且创建了一个grain类的实例,称为grain激活,
    • 如果适用,grain的构造函数将利用依赖注入执行
    • 如果使用持久化(Declarative Persistence),则从存储中读取grain状态
    • 如果被覆盖,OnActivateAsync则被调用
  • grain处理传入的请求
  • grain闲置一段时间
  • Silo运行时决定释放grain
  • Silo运行时调用OnDeactivateAsync,如果被覆盖
  • Silo运行时从内存中移除grain

在一个silo的正常关闭后,它所持有的所有grain激活都会被释放。等待在grains队列中处理的任何请求,被转发到群集中的其他silos,其中在需要的基础上创建被释放的grains的新激活。如果一个silo关闭或不合理地停止,群集中的其他silos会检测到故障,并开始创建在故障silo上的grains的新激活,并且这些grains是作为新的请求。请注意,检测silo故障需要一些时间(可配置),因此重新激活丢失的grains的过程不是即时的。

Grain的执行

grain激活以块的形式执行,并在每个块移动到下一个块之前完成。大量工作包括响应来自其他grains或外部客户端的请求的方法调用,以及在完成前一个块时安排的闭包。对应于一大块工作的基本执行单元称为turn。

虽然Orleans可以并行执行属于不同激活的许多turn,但每次激活总是一次执行一次。这意味着不需要使用锁,或其他同步方法来防止数据争用,和其他多线程危险。

推荐文档