You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何确保Orleans中同一游戏实例的关联Grain在同一个Silo激活

Orleans 同游戏关联Grain共置Silo实现方案

核心实现思路

你需要通过自定义Grain放置策略,让所有关联同一个GameGuid的Grain都绑定到GameGrain所在的Silo上,具体实现步骤如下:


步骤1:实现自定义放置策略类

首先继承Orleans的PlacementStrategy基类,定义游戏关联放置策略,仅作为标记使用无需额外逻辑:

public class GameAffinityPlacementStrategy : PlacementStrategy
{
}

步骤2:实现放置定向器

继承IPlacementDirector<GameAffinityPlacementStrategy>,实现核心路由逻辑:

public class GameAffinityPlacementDirector : IPlacementDirector<GameAffinityPlacementStrategy>
{
    private readonly IManagementGrain _managementGrain;
    private readonly IGrainFactory _grainFactory;

    public GameAffinityPlacementDirector(IManagementGrain managementGrain, IGrainFactory grainFactory)
    {
        _managementGrain = managementGrain;
        _grainFactory = grainFactory;
    }

    public async Task<SiloAddress> OnAddActivation(PlacementStrategy strategy, PlacementTarget target, IPlacementContext context)
    {
        // 从当前Grain主键提取GameGuid
        var gameId = ExtractGameIdFromGrainKey(target.GrainId);
        if (gameId == Guid.Empty)
        {
            // 提取失败则降级为默认放置逻辑
            return context.GetCompatibleSilos(target).First();
        }

        // 获取对应GameGrain的激活位置
        var gameGrain = _grainFactory.GetGrain<IGameGrain>(gameId);
        var gameActivations = await _managementGrain.GetGrainActivations(new[] { gameGrain.GetGrainId() });
        
        if (gameActivations.Any())
        {
            // GameGrain已激活,直接返回其所在Silo地址
            return gameActivations.First().SiloAddress;
        }

        // GameGrain未激活时按默认规则选Silo,后续GameGrain激活时会遵循同策略保持位置一致
        return context.GetCompatibleSilos(target).First();
    }

    // 从Grain主键提取GameGuid的工具方法
    private Guid ExtractGameIdFromGrainKey(GrainId grainId)
    {
        var keyStr = grainId.Key.ToString();
        // 适配PlayerGrain主键格式:<GameGuid>-<PlayerGuid>
        if (keyStr.Contains('-') && keyStr.Split('-').Length == 2)
        {
            if (Guid.TryParse(keyStr.Split('-')[0], out var gameId))
            {
                return gameId;
            }
        }
        // 适配GameGrain/WeaponsGrain主键格式:直接为GameGuid
        if (Guid.TryParse(keyStr, out var directGameId))
        {
            return directGameId;
        }
        return Guid.Empty;
    }
}

步骤3:为需要共置的Grain绑定策略

给三个需要同Silo激活的Grain加上Placement特性,指定使用自定义策略:

[Placement(typeof(GameAffinityPlacementStrategy))]
public class GameGrain : Grain, IGameGrain
{
    // 业务逻辑
}

[Placement(typeof(GameAffinityPlacementStrategy))]
public class PlayerGrain : Grain, IPlayerGrain
{
    // 业务逻辑
}

[Placement(typeof(GameAffinityPlacementStrategy))]
public class WeaponsGrain : Grain, IWeaponsGrain
{
    // 业务逻辑
}

步骤4:Silo启动时注册策略

修改Silo配置代码,注册自定义的放置策略和定向器:

var builder = Host.CreateDefaultBuilder(args)
    .UseOrleans(siloBuilder =>
    {
        // 其他Silo配置
        siloBuilder.AddPlacementDirector<GameAffinityPlacementStrategy, GameAffinityPlacementDirector>();
    });

异常场景适配

如果出现Silo宕机导致GameGrain迁移的情况,该策略会自动识别新的GameGrain激活位置,后续新拉起的关联Grain会自动绑定到新Silo,无需额外手动处理迁移逻辑。

内容的提问来源于stack exchange,提问作者Monku

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 11:30:01