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) 接口到实现的解析:GrainInterfaceTypeResolverGrainInterfaceTypeToGrainTypeResolver

  • 接口类型解析:
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,保存 GrainInterfaceTypeIGrainReferenceRuntime
    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
    • IHelloGrainGrainInterfaceType
    • 解析实现 → GrainType
    • 合成 GrainId = GrainType + Key(friend)
    • GrainReferenceActivator 生成 GrainReference
  • 延迟调用:对 friend 调用方法时,GrainReference 将请求交给 GrainReferenceRuntime,它通过 IRuntimeClient.SendRequest(...) 将消息发往目标激活,期间经历目录定位、消息路由、序列化、过滤链等。

7) 关键点与常见疑问

  • 引用不是实例化:GetGrain 不会立即激活 Grain;引用是可序列化的代理,真正的网络交互在首次方法调用时发生。
  • 主键如何影响定位:字符串主键构成 GrainId.Key,与 GrainType 一起决定放置与目录定位;不同主键对应不同实例身份。
  • 多实现选择:若同一接口有多个实现,可通过 grainClassNamePrefix 决定绑定的 Grain 类型(实现类名前缀匹配)。

8) 小结

  • GetGrain<IHelloGrain>("friend") 的核心工作:接口类型解析、实现类型解析、主键封装、GrainId 构造、GrainReference 生成。
  • 方法调用时才触发网络请求,由 GrainReferenceRuntimeIRuntimeClient 发送。

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 到一次方法调用)

应用代码Host DI 容器GrainFactoryGrainInterfaceTypeResolverGrainInterfaceTypeToGrainTypeResolverGrainReferenceActivatorGrainReferenceGrainReferenceRuntimeIRuntimeClientGetRequiredService<IGrainFactory>()1GrainFactory 实例2GetGrain<IHelloGrain>("friend")3IdSpan.Create("friend")4GetGrainInterfaceType(typeof(IHelloGrain))5GrainInterfaceType6GetGrainType(interfaceType, prefix)7GetGrainType(interfaceType)8alt[指定 prefix][无 prefix]GrainType9GrainId.Create(GrainType, Key)10CreateReference(GrainId, GrainInterfaceType)11GrainReference (Ref)12IHelloGrain 引用 (Ref)13仅创建代理,不触发远程调用调用方法(例如 SayHello("x"))14InvokeAsync(Request)15SendRequest(Ref, Request, Completion)16异步响应(或 OneWay 无响应)17结果/完成18返回任务/结果19应用代码Host DI 容器GrainFactoryGrainInterfaceTypeResolverGrainInterfaceTypeToGrainTypeResolverGrainReferenceActivatorGrainReferenceGrainReferenceRuntimeIRuntimeClient

11) 关键类关系简图

创建
GrainFactory
- GrainReferenceActivator referenceActivator
- GrainInterfaceTypeResolver interfaceTypeResolver
- GrainInterfaceTypeToGrainTypeResolver interfaceTypeToGrainTypeResolver
+GetGrain(string key)
-GetGrain(Type, IdSpan, string)
GrainInterfaceTypeResolver
+GetGrainInterfaceType(Type)
GrainInterfaceTypeToGrainTypeResolver
+GetGrainType(GrainInterfaceType)
+GetGrainType(GrainInterfaceType, string)
GrainReferenceActivator
+CreateReference(GrainId, GrainInterfaceType)
GrainReference
+ GrainId: GrainId
+ InterfaceType: GrainInterfaceType
+InvokeAsync(IRequest)
GrainReferenceRuntime
+InvokeMethodAsync(GrainReference, IInvokable, InvokeMethodOptions)
IRuntimeClient
+SendRequest(GrainReference, IInvokable, IResponseCompletionSource, InvokeMethodOptions)

12) 端到端要点回顾

  • GetGrain<T>(string) 只创建引用,不会立即激活 Grain。
  • 接口类型解析与实现选择基于运行时元数据(部分来自编译期生成代码)。
  • 真正的远程交互发生在方法调用时,由 GrainReferenceRuntimeIRuntimeClient 协作完成。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

helloworddm

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值