粒度标识
Orleans 中的每个粒度都有单个独一无二的、用户定义的标识符,该标识符由两部分组成:
- 粒度类型名称,用于对粒度类进行独一无二的标识。
- 粒度键,用于对该粒度类的逻辑实例进行独一无二的标识。
粒度类型和键都在 Orleans 中表示为人类可读的字符串。按照惯例,粒度标识是用由 /
字符分隔的粒度类型和键编写的。 例如,shoppingcart/bob65
表示名为 shoppingcart
且键为 bob65
的粒度类型。
直接构造粒度标识的情况并不常见。 更常见的是使用 Orleans.IGrainFactory 创建粒度引用。
以下部分更详细地讨论粒度类型名称和粒度键。
粒度类型名称
Orleans 会根据粒度实现类为你创建一个粒度类型名称,方法是从类名称中删除后缀“Grain”(如果存在),并将生成的字符串转换为其小写表示形式。 例如,名为 ShoppingCartGrain
的类将被赋予粒度类型名称 shoppingcart
。 建议让粒度类型名称和键仅包含可打印字符,例如字母数字(a
-z
、A
-Z
和 0
-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 还公开了允许从 Guid 或 Int64 构造粒度键的方法。 主键的范围限定为粒度类型。 因此,粒度的完整标识由粒度的类型及其键构成。
粒度的调用方决定应使用哪种方案。 选项包括:
由于基础数据相同,因此方案可以互换使用:它们都被编码为字符串。。
需要单一实例粒度实例的情况可以使用众所周知的固定值,例如 "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 和字符串的组合来引用粒度。
可以从 IGrainWithGuidCompoundKey 或 IGrainWithIntegerCompoundKey 接口继承你的接口,如下所示:
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 使用逻辑标识符来避免此限制。 粒度使用逻辑标识符,以便粒度引用在整个进程生命周期中保持有效,并且可以从一个进程移植到另一个进程,使它们能够被存储并稍后进行检索,或通过网络发送到应用程序中的另一个进程,同时仍然引用同一实体:为其创建引用的粒度。