粒度标识

Orleans 中的每个粒度都有单个独一无二的、用户定义的标识符,该标识符由两部分组成:

  1. 粒度类型名称,用于对粒度类进行独一无二的标识。
  2. 粒度键,用于对该粒度类的逻辑实例进行独一无二的标识。

粒度类型和键都在 Orleans 中表示为人类可读的字符串。按照惯例,粒度标识是用由 / 字符分隔的粒度类型和键编写的。 例如,shoppingcart/bob65 表示名为 shoppingcart 且键为 bob65 的粒度类型。

直接构造粒度标识的情况并不常见。 更常见的是使用 Orleans.IGrainFactory 创建粒度引用

以下部分更详细地讨论粒度类型名称和粒度键。

粒度类型名称

Orleans 会根据粒度实现类为你创建一个粒度类型名称,方法是从类名称中删除后缀“Grain”(如果存在),并将生成的字符串转换为其小写表示形式。 例如,名为 ShoppingCartGrain 的类将被赋予粒度类型名称 shoppingcart。 建议让粒度类型名称和键仅包含可打印字符,例如字母数字(a-zA-Z0-9)字符和符号,如 -_@=。 其他字符可能受支持,也可能不受支持,在日志中打印或在其他系统(例如数据库)中作为标识符出现时通常需要特殊处理。

也可使用 Orleans.GrainTypeAttribute 属性为其附加到的粒度类自定义粒度类型名称,如下例所示:

[GrainType("cart")]
public class ShoppingCartGrain : IShoppingCartGrain
{
    // Add your grain implementation here
}

在上一示例中,粒度类 ShoppingCartGrain 的粒度类型名称为 cart。 每个粒度只能有一个粒度类型名称。

对于泛型粒度,泛型元数必须包含在粒度类型名称中。 例如,考虑以下 DictionaryGrain<K, V> 类:

[GrainType("dict`2")]
public class DictionaryGrain<K, V> : IDictionaryGrain<K, V>
{
    // Add your grain implementation here
}

粒度类有两个泛型参数,因此请将反引号 `(后跟泛型元数 2)添加到粒度类型名称 dict 的末尾,以创建粒度类型名称 dict`2,如粒度类 [GrainType("dict`2")] 的属性中指定的那样。

粒度键

为方便起见,除了 String 之外,Orleans 还公开了允许从 GuidInt64 构造粒度键的方法。 主键的范围限定为粒度类型。 因此,粒度的完整标识由粒度的类型及其键构成。

粒度的调用方决定应使用哪种方案。 选项包括:

由于基础数据相同,因此方案可以互换使用:它们都被编码为字符串。。

需要单一实例粒度实例的情况可以使用众所周知的固定值,例如 "default"。 这只是一个约定,但通过遵守此约定,在调用方站点上就会很明显发现单一实例粒度正在使用中。

使用全局唯一标识符 (GUID) 作为键

需要随机性和全局唯一性时(例如在作业处理系统中创建新作业时),System.Guid 会生成有用的键。 无需协调键的分配(这可能会在系统中引入单一故障点),也无需协调资源上可能导致出现瓶颈的系统端锁。 GUID 冲突的可能性非常低,因此在构建需要分配随机标识符的系统时,它们是常见的选择。

在客户端代码中按 GUID 引用粒度:

var grain = grainFactory.GetGrain<IExample>(Guid.NewGuid());

从粒度代码检索主键:

public override Task OnActivateAsync()
{
    Guid primaryKey = this.GetPrimaryKey();
    return base.OnActivateAsync();
}

使用整数作为键

长整型也可用,如果粒度保留到关系数据库,其中数字索引优先于 GUID,则这一点会有意义。

在客户端代码中按长整型引用粒度:

var grain = grainFactory.GetGrain<IExample>(1);

从粒度代码检索主键:

public override Task OnActivateAsync()
{
    long primaryKey = this.GetPrimaryKeyLong();
    return base.OnActivateAsync();
}

使用字符串作为键

字符串也可用。

在客户端代码中按字符串引用粒度:

var grain = grainFactory.GetGrain<IExample>("myGrainKey");

从粒度代码检索主键:

public override Task OnActivateAsync()
{
    string primaryKey = this.GetPrimaryKeyString();
    return base.OnActivateAsync();
}

使用复合键

如果系统不适合 GUID 或 long,则可以选择复合主键,这样就可以使用 GUID 或 long 和字符串的组合来引用粒度。

可以从 IGrainWithGuidCompoundKeyIGrainWithIntegerCompoundKey 接口继承你的接口,如下所示:

public interface IExampleGrain : Orleans.IGrainWithIntegerCompoundKey
{
    Task Hello();
}

在客户端代码中,这会向粒度工厂上的 IGrainFactory.GetGrain 方法添加第二个参数:

var grain = grainFactory.GetGrain<IExample>(0, "a string!", null);

若要访问粒度中的复合键,我们可以对 GrainExtensions.GetPrimaryKey 方法 (GrainExtensions.GetPrimaryKeyLong) 调用重载:

public class ExampleGrain : Orleans.Grain, IExampleGrain
{
    public Task Hello()
    {
        long primaryKey = this.GetPrimaryKeyLong(out string keyExtension);
        Console.WriteLine($"Hello from {keyExtension}");

        Task.CompletedTask;
    }
}

为什么粒度使用逻辑标识符

在面向对象的环境(例如 .NET)中,对象的标识很难与对对象的引用区分开来。 使用 new 关键字创建对象时,返回的引用表示其标识的所有方面,但将对象映射到它所表示的某些外部实体除外。 Orleans 专为分布式系统设计。 在分布式系统中,对象引用不能表示实例标识,因为对象引用仅限于单个进程的地址空间。 Orleans 使用逻辑标识符来避免此限制。 粒度使用逻辑标识符,以便粒度引用在整个进程生命周期中保持有效,并且可以从一个进程移植到另一个进程,使它们能够被存储并稍后进行检索,或通过网络发送到应用程序中的另一个进程,同时仍然引用同一实体:为其创建引用的粒度。