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

相关推荐
tang_04274 小时前
【Hi.Ltd 专题】第7期:Threading 采集线程、锁与 LRU 缓存
经验分享·c#·hi.ltd系列
A_nanda4 小时前
C# 界面卡顿:从定位到根治
c#
2501_930707785 小时前
使用C#代码为新创建的 Word 文档创建目录
c#·word
czhc11400756636 小时前
同一个数,两把尺子:六组“看着一样、其实不一样“
c#
花北城7 小时前
【C#底层库】access_token授权鉴权验证
c#·鉴权·token·授权
rick9777 小时前
C# 动态代理与 DispatchProxy:从原理到实战的完整指南
c#
tang_04278 小时前
【Hi.Ltd 专题】第8期:Managements 插件、单例与服务定位
经验分享·c#·hi.ltd系列
曹牧18 小时前
C#:24小时制时间
c#
唐青枫1 天前
别急着拆服务:C#.NET 微服务架构从边界设计到实战
c#·.net
czhc11400756631 天前
从一份运行日志还原一次生产卡死:三个信号与一场“假卡死“
c#