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

相关推荐
吴可可1239 小时前
多段线局部约束缩放实现
c#
棉晗榜13 小时前
C# HttpClient配置具有tls指纹发起跟浏览器一样的请求解决反爬虫防护
开发语言·爬虫·c#
吴可可12314 小时前
在CAD中点击添加多段线顶点的方法与实现
c#
全栈小514 小时前
【C#】.net core,静态方法线程安全解析,面试时经常被问的线程安全就藏在里面
安全·c#·.netcore
天下无敌笨笨熊15 小时前
C#视频开发心得
开发语言·c#·音视频
LccKyI15 小时前
C#基础编程 面试题汇总(三)
笔记·c#
半亩码田17 小时前
C#转Python第4.1篇:当 try-catch 遇上 try-except:异常处理的大不同
开发语言·python·c#
z落落18 小时前
C# Modbus-RTU 串口读写设备+CRC16+邮件告警
开发语言·c#
DS随心转小程序18 小时前
Gemini只能导出了一部分Word文档,很少的一部分,不是我这个窗口所有的对话内容?
开发语言·人工智能·c#·word·豆包·deepseek·ai导出鸭
格林威18 小时前
C# 相机图像配合频闪光源:实现高速稳定拍摄的几个方法
开发语言·网络·人工智能·数码相机·计算机视觉·c#·视觉检测