Orleans GetGrain<T>(string) 底层原理
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 precedenceforeach (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 checkif (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协作完成。
