如何确保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
相关产品推荐
相关产品推荐

