Orleans GetGrain(string) 底层原理(结合源码)
本文解析以下调用的底层流程,并给出关键源码引用:
// 获取 GrainFactory
var grainFactory = host.Services.GetRequiredService<IGrainFactory>();
// 获取 IHelloGrain 的引用,主键为字符串 "friend"
var friend = grainFactory.GetGrain<IHelloGrain>("friend");
1) IGrainFactory 暴露的 API(定义)
using System;
using System.Threading.Tasks;
using Orleans.Runtime;
namespace Orleans
{
/// <summary>
/// Functionality for creating references to grains.
/// </summary>
public interface IGrainFactory
{
/// <summary>
/// Gets a reference to a grain.
/// </summary>
/// <typeparam name="TGrainInterface">The interface type.</typeparam>
/// <param name="primaryKey">The primary key of the grain.</param>
/// <param name="grainClassNamePrefix">An optional class name prefix used to find the runtime type of the grain.</param>
/// <returns>A reference to the specified grain.</returns>
TGrainInterface GetGrain<TGrainInterface>(Guid primaryKey, string grainClassNamePrefix = null) where TGrainInterface : IGrainWithGuidKey;
/// <summary>
/// Gets a reference to a grain.
/// </summary>
/// <typeparam name="TGrainInterface">The interface type.</typeparam>
/// <param name="primaryKey">The primary key of the grain.</param>
/// <param name="grainClassNamePrefix">An optional class name prefix used to find the runtime type of the grain.</param>
/// <returns>A reference to the specified grain.</returns>
TGrainInterface GetGrain<TGrainInterface>(long primaryKey, string grainClassNamePrefix = null) where TGrainInterface : IGrainWithIntegerKey;
/// <summary>
/// Gets a reference to a grain.
/// </summary>
/// <typeparam name="TGrainInterface">The interface type.</typeparam>
/// <param name="primaryKey">The primary key of the grain.</param>
/// <param name="grainClassNamePrefix">An optional class name prefix used to find the runtime type of the grain.</param>
/// <returns>A reference to the specified grain.</returns>
TGrainInterface GetGrain<TGrainInterface>(string primaryKey, string grainClassNamePrefix = null) where TGrainInterface : IGrainWithStringKey;
/// <summary>
/// Gets a reference to a grain.
要点:GetGrain<T>(string) 是强类型接口,参数 primaryKey 为字符串主键。
2) GrainFactory 实现:字符串主键到 GrainReference
- 入口:
GetGrain<TGrainInterface>(string primaryKey, ...)将字符串转换为IdSpan,再走私有重载。
/// <inheritdoc />
public TGrainInterface GetGrain<TGrainInterface>(long primaryKey, string grainClassNamePrefix = null) where TGrainInterface : IGrainWithIntegerKey
{
var grainKey = GrainIdKeyExtensions.CreateIntegerKey(primaryKey);
return (TGrainInterface)GetGrain(typeof(TGrainInterface), grainKey, grainClassNamePrefix: grainClassNamePrefix);
}
/// <inheritdoc />
public TGrainInterface GetGrain<TGrainInterface>(string primaryKey, string grainClassNamePrefix = null)
where TGrainInterface : IGrainWithStringKey
{
var grainKey = IdSpan.Create(primaryKey);
return (TGrainInterface)GetGrain(typeof(TGrainInterface), grainKey, grainClassNamePrefix: grainClassNamePrefix);
}
- 关键:私有方法将接口类型映射到实现
GrainType,合成GrainId,再通过GrainReferenceActivator创建引用。
/// <summary>
/// Gets a grain reference which implements the specified grain interface type and has the specified grain key, without specifying the grain type directly.
/// </summary>
/// <remarks>
/// This method infers the most appropriate <see cref="GrainId.Type"/> value based on the <paramref name="interfaceType"/> argument and optional <paramref name="grainClassNamePrefix"/> argument.
/// The <see cref="GrainInterfaceTypeToGrainTypeResolver"/> type is responsible for determining the most appropriate grain type.
/// </remarks>
/// <param name="interfaceType">The interface type which the returned grain reference will implement.</param>
/// <param name="grainKey">The <see cref="GrainId.Key"/> portion of the grain id.</param>
/// <param name="grainClassNamePrefix">An optional grain class name prefix.</param>
/// <returns>A grain reference which implements the provided interface.</returns>
private IAddressable GetGrain(Type interfaceType, IdSpan grainKey, string grainClassNamePrefix)
{
var grainInterfaceType = this.interfaceTypeResolver.GetGrainInterfaceType(interfaceType);
GrainType grainType;
if (!string.IsNullOrWhiteSpace(grainClassNamePrefix))
{
grainType = this.interfaceTypeToGrainTypeResolver.GetGrainType(grainInterfaceType, grainClassNamePrefix);
}
else
{
grainType = this.interfaceTypeToGrainTypeResolver.GetGrainType(grainInterfaceType);
}
var grainId = GrainId.Create(grainType, grainKey);
var grain = this.referenceActivator.CreateReference(grainId, grainInterfaceType);
return grain;
}
要点:
- 字符串主键通过
IdSpan.Create(primaryKey)成为GrainId的 Key 部分。 GrainInterfaceTypeResolver将接口类型(如IHelloGrain)转换为GrainInterfaceType。GrainInterfaceTypeToGrainTypeResolver找到实际的GrainType(某个实现类)。GrainId.Create(grainType, grainKey)形成稳定标识,GrainReferenceActivator.CreateReference(...)返回可调用的代理引用。
3) 接口到实现的解析:GrainInterfaceTypeResolver 与 GrainInterfaceTypeToGrainTypeResolver
- 接口类型解析:
using System;
using System.Collections.Generic;
using System.Linq;
using Orleans.Runtime;
using Orleans.Serialization.TypeSystem;
namespace Orleans.Metadata
{
/// <summary>
/// Associates a <see cref="GrainInterfaceType"/> with a <see cref="Type" />.
/// </summary>
public class GrainInterfaceTypeResolver
{
private readonly IGrainInterfaceTypeProvider[] _providers;
private readonly TypeConverter _typeConverter;
...
public GrainInterfaceType GetGrainInterfaceType(Type type)
{
if (!type.IsInterface)
{
throw new ArgumentException($"Argument {nameof(type)} must be an interface. Provided value, \"{type}\", is not an interface.", nameof(type));
}
// Configured providers take precedence
foreach (var provider in _providers)
{
if (provider.TryGetGrainInterfaceType(type, out var interfaceType))
{
interfaceType = AddGenericParameters(interfaceType, type);
return interfaceType;
}
}
// Conventions are used as a fallback.
return GetGrainInterfaceTypeByConvention(type);
}
- 接口到实现类型映射:
/// <summary>
/// Associates <see cref="GrainInterfaceType"/>s with a compatible <see cref="GrainType"/>.
/// </summary>
/// <remarks>
/// This is primarily intended for end-users calling <see cref="IGrainFactory"/> methods without needing to be overly explicit.
/// </remarks>
public class GrainInterfaceTypeToGrainTypeResolver
{
...
/// <summary>
/// Returns the <see cref="GrainType"/> which supports the provided <see cref="GrainInterfaceType"/> and which has an implementing type name beginning with the provided prefix string.
/// </summary>
public GrainType GetGrainType(GrainInterfaceType interfaceType, string prefix)
{
if (string.IsNullOrWhiteSpace(prefix))
要点:运行时使用集群清单/Provider 解析接口到实现,支持泛型与前缀选择。
4) GrainReference:引用的结构与调用入口
GrainReference组合GrainType + IdSpan(Key)形成GrainId,保存GrainInterfaceType与IGrainReferenceRuntime。
public class GrainReference : IAddressable, IEquatable<GrainReference>, ISpanFormattable
{
/// <summary>
/// The grain reference functionality which is shared by all grain references of a given type.
/// </summary>
[NonSerialized]
private readonly GrainReferenceShared _shared;
/// <summary>
/// The underlying grain id key.
/// </summary>
[NonSerialized]
private readonly IdSpan _key;
...
/// <summary>
/// Gets the grain id.
/// </summary>
public GrainId GrainId => GrainId.Create(_shared.GrainType, _key);
/// <summary>
/// Gets the interface type.
/// </summary>
public GrainInterfaceType InterfaceType => _shared.InterfaceType;
...
protected GrainReference(GrainReferenceShared shared, IdSpan key)
{
_shared = shared;
_key = key;
}
- 方法调用由
GrainReference.InvokeAsync(...)委派到IGrainReferenceRuntime:
/// <summary>
/// Invokes the provided method.
/// </summary>
/// <typeparam name="T">The underlying method return type.</typeparam>
/// <param name="methodDescription">The method description.</param>
/// <returns>The result of the invocation.</returns>
protected ValueTask<T> InvokeAsync<T>(IRequest methodDescription)
{
return this.Runtime.InvokeMethodAsync<T>(this, methodDescription, methodDescription.Options);
}
...
protected void Invoke(IRequest methodDescription)
{
this.Runtime.InvokeMethod(this, methodDescription, methodDescription.Options);
}
要点:friend 是一个轻量代理;真正的远程调用在后续方法执行时才发生。
5) GrainReferenceRuntime:发送请求到运行时
- 调用被包装为
IInvokable,通过IRuntimeClient.SendRequest(...)派发:
internal class GrainReferenceRuntime : IGrainReferenceRuntime
{
private readonly GrainReferenceActivator referenceActivator;
private readonly GrainInterfaceTypeResolver interfaceTypeResolver;
private readonly IGrainCancellationTokenRuntime cancellationTokenRuntime;
private readonly IOutgoingGrainCallFilter[] filters;
private readonly Action<GrainReference, IResponseCompletionSource, IInvokable, InvokeMethodOptions> sendRequest;
public GrainReferenceRuntime(
IRuntimeClient runtimeClient,
IGrainCancellationTokenRuntime cancellationTokenRuntime,
IEnumerable<IOutgoingGrainCallFilter> outgoingCallFilters,
GrainReferenceActivator referenceActivator,
GrainInterfaceTypeResolver interfaceTypeResolver)
{
this.RuntimeClient = runtimeClient;
this.cancellationTokenRuntime = cancellationTokenRuntime;
this.referenceActivator = referenceActivator;
this.interfaceTypeResolver = interfaceTypeResolver;
this.filters = outgoingCallFilters.ToArray();
this.sendRequest = (GrainReference reference, IResponseCompletionSource callback, IInvokable body, InvokeMethodOptions options) => RuntimeClient.SendRequest(reference, body, callback, options);
}
- 发送逻辑(无过滤器时直发):
public ValueTask<TResult> InvokeMethodAsync<TResult>(GrainReference reference, IInvokable request, InvokeMethodOptions options)
{
// TODO: Remove expensive interface type check
if (this.filters.Length == 0 && request is not IOutgoingGrainCallFilter)
{
SetGrainCancellationTokensTarget(reference, request);
var responseCompletionSource = ResponseCompletionSourcePool.Get<TResult>();
this.RuntimeClient.SendRequest(reference, request, responseCompletionSource, options);
return responseCompletionSource.AsValueTask();
}
6) 端到端流程(简述)
- 获取服务:
host.Services.GetRequiredService<IGrainFactory>()从 DI 容器取得GrainFactory实例(宿主初始化时注册)。 - 构建引用:
GetGrain<IHelloGrain>("friend")- 将 “friend” →
IdSpan - 将
IHelloGrain→GrainInterfaceType - 解析实现 →
GrainType - 合成
GrainId = GrainType + Key(friend) GrainReferenceActivator生成GrainReference
- 将 “friend” →
- 延迟调用:对
friend调用方法时,GrainReference将请求交给GrainReferenceRuntime,它通过IRuntimeClient.SendRequest(...)将消息发往目标激活,期间经历目录定位、消息路由、序列化、过滤链等。
7) 关键点与常见疑问
- 引用不是实例化:
GetGrain不会立即激活 Grain;引用是可序列化的代理,真正的网络交互在首次方法调用时发生。 - 主键如何影响定位:字符串主键构成
GrainId.Key,与GrainType一起决定放置与目录定位;不同主键对应不同实例身份。 - 多实现选择:若同一接口有多个实现,可通过
grainClassNamePrefix决定绑定的 Grain 类型(实现类名前缀匹配)。
8) 小结
GetGrain<IHelloGrain>("friend")的核心工作:接口类型解析、实现类型解析、主键封装、GrainId构造、GrainReference生成。- 方法调用时才触发网络请求,由
GrainReferenceRuntime经IRuntimeClient发送。
9) 生成代码 HelloWorld.orleans.g.cs 的生成时机与作用
- 生成时机:编译期由 Orleans 的 Roslyn Source Generator 运行,向编译管线追加生成源,文件名为
<AssemblyName>.orleans.g.cs(例如项目程序集名为 HelloWorld,则为HelloWorld.orleans.g.cs)。
[Generator]
public class OrleansSerializationSourceGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
...
var codeGenerator = new CodeGenerator(context.Compilation, options);
var syntax = codeGenerator.GenerateCode(context.CancellationToken);
var sourceString = syntax.NormalizeWhitespace().ToFullString();
var sourceText = SourceText.From(sourceString, Encoding.UTF8);
context.AddSource($"{context.Compilation.AssemblyName ?? "assembly"}.orleans.g.cs", sourceText);
}
}
- 生成内容:接口代理(Proxy)、方法 Invokable、序列化器、元数据、激活器等。按需发射生成(如果编译集中不存在对应类型则注入):
if (Compilation.GetTypeByMetadataName(generatedInvokable.MetadataName) == null)
{
// Emit the generated code on-demand.
AddMember(generatedInvokable.GeneratedNamespace, generatedInvokable.ClassDeclarationSyntax);
// Ensure the type will have a serializer generated for it.
MetadataModel.SerializableTypes.Add(generatedInvokable);
...
}
- 在运行时如何被使用:
- 第 2/3 步的接口到实现解析会使用生成的元数据(帮助从接口映射到实现类型)。
- 方法调用时,
GrainReference获取并使用生成的 Invokable 类型打包调用请求,再交由GrainReferenceRuntime发送:
protected TInvokable GetInvokable<TInvokable>() => ActivatorUtilities.GetServiceOrCreateInstance<TInvokable>(Shared.ServiceProvider);
protected ValueTask<T> InvokeAsync<T>(IRequest methodDescription)
{
return this.Runtime.InvokeMethodAsync<T>(this, methodDescription, methodDescription.Options);
}
10) 时序图(从 GetGrain 到一次方法调用)
11) 关键类关系简图
12) 端到端要点回顾
GetGrain<T>(string)只创建引用,不会立即激活 Grain。- 接口类型解析与实现选择基于运行时元数据(部分来自编译期生成代码)。
- 真正的远程交互发生在方法调用时,由
GrainReferenceRuntime和IRuntimeClient协作完成。

235

被折叠的 条评论
为什么被折叠?



