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没有启动服务,客户端调用没有明显卡顿,直接抛异常。

相关推荐
进击的大海贼15 小时前
基于C#开发的久坐提醒工具
开发语言·c#
小羊没烦恼!17 小时前
关于大型asp.net应用系统的架构-架构的选择
java·服务器·开发语言·前端·c#
小羊没烦恼!1 天前
系统内部模块(子系统)之间的耦合以及模块(子系统)划分
java·开发语言·前端·数据库·算法·c#
UIU1142 天前
头文件可以防什么错?运用头文件做声明
c++·学习·c#
鑫 源2 天前
C#源码开源, 获取macOS 电池数据,包括iOS (xamarin开发)
c#·.net·xamarin
爱吃奥利奥_wen2 天前
面向对象三板斧:封装、继承、多态,到底在“装“什么、“承“什么、“变“什么?
开发语言·经验分享·c#
wflynn3 天前
GitHub 今日推荐|SCSKiller:游戏着色器预编译消除卡顿
开源·c#·github·nvidia·amd·directx12
疯狂的维修3 天前
关于用C#写电机起保停控制
开发语言·c#
用户788477316343 天前
门店核销闭环:小程序扫码 + .NET 幂等接口 + WPF 看板,一天能跑通
微信小程序·c#