Orleans10的非对称部署(部署更友好的方式)

创建定义所有接口的库(一个),但需分别在不同的库中实现接口;利用Orleans服务自动发现已加载程序集中的所有Gain的特性,在进程启动时,先动态加载Gain的实现程序集,在启动silo服务时,控制当前silo启动哪个Gain。

启动Silo前使用Assembly.LoadFrom方法调用选择的程序集。

cs 复制代码
            // 非对称部署(物理分离方案):Orleans 7.0+ 移除了 GrainClassOptions.ExcludedGrainTypes,
            // 改用"运行时按角色加载 Grain 程序集"实现------只有被 Assembly.LoadFrom 加载进 AppDomain
            // 的 Grain 实现才会被 Silo 发现并托管。HelloGrain 和 WeatherGrain 分别在独立的
            // Grains.Hello.dll / Grains.Weather.dll 中(由 SiloHost.csproj 的 CopyGrainDlls target 复制)。
            LoadGrainAssemblies(role);

            var builder = Host.CreateApplicationBuilder();
            builder.Logging.AddProvider(new RichTextLoggerProvider(_logBox));
            builder.Logging.AddFilter("Orleans", LogLevel.Information);

            // 让每个 Grain 能报告自己运行在哪个 Silo(验证非对称部署)
            builder.Services.AddSingleton<ISiloIdentity>(new SiloIdentity(siloName));

            builder.UseOrleans(siloBuilder =>
            {
                siloBuilder
                    .Configure<ClusterOptions>(options =>
                    {
                        options.ClusterId = ClusterId;
                        options.ServiceId = ServiceId;
                    })
                    .Configure<SiloOptions>(options => options.SiloName = siloName)
                    .ConfigureEndpoints("localhost", siloPort, gatewayPort)
                    .UseRedisClustering(options =>
                    {
                        options.ConfigurationOptions = ConfigurationOptions.Parse(redisConn);
                    });
            });

            _host = builder.Build();
            await _host.StartAsync();
            Log($"Silo '{siloName}' 已加入集群 '{ClusterId}'。");
            _stopBtn.Enabled = true;
cs 复制代码
        try
        {
            var redisConn = _redisBox.Text.Trim();
            Log($"通过 Redis '{redisConn}' 连接集群 '{ClusterId}' ...");

            var builder = Host.CreateApplicationBuilder();
            builder.UseOrleansClient(clientBuilder =>
            {
                clientBuilder
                    .Configure<ClusterOptions>(options =>
                    {
                        options.ClusterId = ClusterId;
                        options.ServiceId = ServiceId;
                    })
                    .UseRedisClustering(options =>
                    {
                        options.ConfigurationOptions = ConfigurationOptions.Parse(redisConn);
                    });
                    // Orleans 7.0+ 已移除 Application Parts,改由 Source Generator 自动发现 Grain 类型。
                    // Client 项目已通过 ProjectReference 引用 Contracts 程序集,无需手动注册。
            });

            _host = builder.Build();
            await _host.StartAsync();
            _client = _host.Services.GetRequiredService<IClusterClient>();
            Log("已连接到集群,可以调用 Grain。");
            _helloBtn.Enabled = true;
            _weatherBtn.Enabled = true;
        }
        catch (Exception ex)
        {
            Log($"连接失败: {ex}");
            _connectBtn.Enabled = true;
            if (_host is not null)
            {
                try { _host.Dispose(); } catch { }
                _host = null;
            }
        }



    private async void OnCallHello(object? sender, EventArgs e)
    {
        if (_client is null) return;
        var name = _nameBox.Text.Trim();
        if (string.IsNullOrEmpty(name)) { Log("请输入名字。"); return; }
        _helloBtn.Enabled = false;
        try
        {
            var grain = _client.GetGrain<IHelloGrain>(name);
            var result = await grain.SayHello(name);
            Log($"HelloGrain -> {result}");
        }
        catch (Exception ex)
        {
            Log($"调用 HelloGrain 失败: {ex.Message}");
        }
        finally
        {
            _helloBtn.Enabled = true;
        }
    }

    private async void OnCallWeather(object? sender, EventArgs e)
    {
        if (_client is null) return;
        var city = _cityBox.Text.Trim();
        if (string.IsNullOrEmpty(city)) { Log("请输入城市。"); return; }
        _weatherBtn.Enabled = false;
        try
        {
            var grain = _client.GetGrain<IWeatherGrain>(city);
            var result = await grain.GetForecast(city);
            Log($"WeatherGrain -> {result}");
        }
        catch (Exception ex)
        {
            Log($"调用 WeatherGrain 失败: {ex.Message}");
        }
        finally
        {
            _weatherBtn.Enabled = true;
        }
    }

如对应Gain没有启动服务,客户端调用没有明显卡顿,直接抛异常。

相关推荐
geovindu2 小时前
CSharp: Recursion Algorithm
开发语言·后端·算法·c#·递归算法
geats人山人海3 小时前
c# 第八章 多态与案例
开发语言·c#
F20226974864 小时前
西门子 PLC 与 C# 通信_项目实战
大数据·c#
qq_33776320195 小时前
国际期货资管系统开发方案|内外盘交易平台源码授权与二次开发支持
java·c语言·开发语言·c++·c#
rockey62715 小时前
基于AScript的JavaScript脚本语言发布啦
javascript·c#·.net·js·script
LuoCore16 小时前
AForge.Video.FFMPEG.dll加载失败解决指南
c#
吴可可12316 小时前
C#元组解构实现变量交换
c#
慧都小妮子20 小时前
SciChart WPF v9.0 更新详解:矢量场图表、堆叠箱线图与 MVVM 轴同步来了
c#·.net·wpf·数据可视化·图表控件·图表