在面向对象环境中,很难将对象的标识与对其的引用区分开。因此在使用new关键字创建对象后,获得的引用本身就完全涵盖了标识信息,除非这个引用在一个外部的map中映射着另外一个实体(这就不是该对象操心的事情了)。
在分布式系统中,由于对象的引用一般情况下只局限于反应某个具体的地址空间,它无法用来作为一个实例的标识了。.NET的引用就是这种情况。进一步讲,Grain实例无论是否被激活(被加载到内存中),都必须有一个唯一标识,以便于它在失活的状态下能够按需激活。因此Grain必须有一个主键(primary key)。该主键可以使一个GUID(Globally Unique Identifier),一个长整型,或者是一个字符串。
主键的作用域限于某个具体的Grain类型。如此以来,Grain完整的标识就由Grain类型和一个主键来组成。
Grain的调用者可以决定主键使用哪种方案,可选的有如下几种:
- long
- GUID
- string
- GUID + string
- long + string
由于本质上这些数据底层是相同的,因此方案可以随时调整。实际上在使用长整型作为主键的方案时,通常会创建一个GUID,然后用0填充缺失的位数。
在使用单例Grain的情况下,比如字典或注册表,可以用Guid.Empty
作为这个单例的主键。这只是一个约定,不过如果遵守约定,可以在调用Grain的地方清楚地知道发生了什么(it becomes clear at the call site that it is what is going on, as we saw in the first tutorial)。
使用GUID
在有多个进程可能会请求一个Grain实例的情况下,比如web应用跑在多个web服务器上,使用GUID作为主键就很实用。你无需协调主键的分配,因为这个工作可能会引起单点故障,或因(竞争)某资源造成系统端的锁,从而成为系统的瓶颈。GUID发生碰撞的可能性极小,因此在构建一个Orleans系统时,使用GUID方案生成Grain的主键可能是一个默认选择。
在客户端代码中通过GUID方案得到一个Grain引用:
var grain = grainFactory.GetGrain<IExample>(Guid.NewGuid());
在Grain代码中检索主键:
public override Task OnActivateAsync()
{
Guid primaryKey = this.GetPrimaryKey();
// ...
return base.OnActivateAsync();
}
使用长整型
当Grain被存储在关系型数据库时,使用长整型方案比较合适,因为在数据库系统中比起GUID,更偏向于使用数字做索引。
在客户端代码中使用长整型方案得到一个Grain引用:
var grain = grainFactory.GetGrain<IExample>(1);
在Grain代码中检索主键:
public override Task OnActivateAsync()
{
long primaryKey = this.GetPrimaryKeyLong();
return base.OnActivateAsync();
}
使用字符串
使用字符串作为主键也是可行的。
在客户端代码中使用字符串方案得到一个Grain引用:
var grain = grainFactory.GetGrain<IExample>("myGrainKey");
在Grain代码中检索主键:
public override Task OnActivateAsync()
{
string primaryKey = this.GetPrimaryKeyString();
return base.OnActivateAsync();
}
使用复合主键(Compound Primary Key)
如果GUID方案和长整型方案都不是很适用于你的系统,可以考虑一下复合主键的方案,即使用GUID、长整型和字符串组合起来作为Grain的主键。
需要让Grain接口继承一下IGrainWithGuidCompoundKey
或IGrainWithIntegerCompoundKey
接口:
public interface IExampleGrain : Orleans.IGrainWithIntegerCompoundKey
{
Task Hello();
}
在客户端代码中,使用GrainFactory
的GetGrain
方法时,可以多传入一个参数:
var grain = grainFactory.GetGrain<IExample>(0, "a string!", null);
可以使用GetPrimaryKey
方法的一个重载来在Grain中访问复合主键:
public class ExampleGrain : Orleans.Grain, IExampleGrain
{
public Task Hello()
{
string keyExtension;
long primaryKey = this.GetPrimaryKeyLong(out keyExtension);
Console.WriteLine("Hello from " + keyExtension);
Task.CompletedTask;
}
}