Orleans 确保当发起Grain调用时,群集中某个服务器的内存中能够有该Grain的实例来处理请求。 如果Grain当前在群集中未处于活动状态,Orleans 将选择一个服务器来激活Grain。 此过程称为 颗粒放置。 放置也是平衡负载的一种方式 Orleans :均匀分布繁忙的粒子有助于在群集中分配工作负荷。
Orleans 的放置过程完全可配置。 从现成的放置策略(例如随机、首选本地和基于负载)中进行选择,或配置自定义逻辑。 这允许完全灵活地决定谷物创建的位置。 例如,将粒放置在靠近其需要操作的资源的服务器上,或靠近与其通信的其他粒。 默认情况下, Orleans 选取一个随机兼容的服务器。
配置 Orleans 的放置策略,以全局范围或按粒度类进行配置。
随机放置
Orleans 从群集中的兼容服务器随机选择服务器。 若要配置此放置策略,请在粒度类中添加RandomPlacementAttribute。
本地放置
如果本地服务器兼容, Orleans 请选择本地服务器;否则,它会选择一个随机服务器。 将 PreferLocalPlacementAttribute 添加到粒度类中以配置此放置策略。
基于哈希的放置
Orleans 将粒度 ID 哈希为一个非负整数,并对兼容服务器的数量进行取模运算。 然后,从按服务器地址排序的兼容服务器列表中选择相应的服务器。 请注意,此位置不能保证在群集成员身份更改时保持稳定。 具体而言,添加、删除或重启服务器可能会改变用于某个 grain ID 的服务器选择。 由于使用该策略放置的粮食会记录在粮食目录中,因此成员身份的变化导致的放置决策的更改通常不会产生明显影响。
通过将HashBasedPlacementAttribute添加到粒子类来配置此放置策略。
基于激活计数的放置
根据最近活跃粒度的数量,此放置策略尝试将新的粒度激活放置在负载最轻的服务器上。 它包括一种机制,其中所有服务器定期将其激活计数发布到所有其他服务器。 然后,位置协调器检查最近报告的激活计数,并基于他在当前服务器上最近的激活情况预测当前的激活数量,从而选择一个预计激活数最少的服务器。 控制器在进行此预测时随机选择多个服务器,以避免多个单独的服务器重载同一服务器。 默认情况下,会随机选择两个服务器,但可以通过此 ActivationCountBasedPlacementOptions值进行配置。
此算法基于 Michael David Mitzenmacher 所著论文《随机负载均衡中两种选择的力量》。 它还在 Nginx 中用于分布式负载均衡,如 NGINX 和“两种选择的力量”一文中所述,Load-Balancing 算法。
通过向粒度类中添加ActivationCountBasedPlacementAttribute来配置此放置策略。
无状态辅助角色放置
无状态辅助角色放置是无状态辅助角色粒度使用的特殊放置策略。 此方案的运作方式与PreferLocalPlacement
几乎完全相同,只不过每台服务器可以有多个相同的粒度激活,并且这些粒度不需要在粒度目录中注册,因为没有必要。
通过将StatelessWorkerAttribute添加到粒度类来配置此放置策略。
基于职能角色的隔离式放置
这是一种确定性的放置策略,将谷物放置在具有特定角色的筒仓上。 通过向粒子类中添加SiloRoleBasedPlacementAttribute 来配置此放置策略。
资源优化的放置
资源优化的放置策略会根据可用内存和 CPU 使用率在孤岛之间均衡粒度激活来尝试优化群集资源。 它将权重分配给运行时统计信息,以便确定不同资源的优先级,并计算每个孤岛的规范化分数。 选择具有最低分数的接收器来放置即将到来的激活。 规范化可确保每个属性按比例地贡献总体分数。 根据不同资源的特定要求和优先级,通过ResourceOptimizedPlacementOptions调整权重。
此外,此放置策略还提供了一个选项,可以为本地筒仓(接收新放置请求的那个)建立一个更强的偏好,以便它被选定为激活的目标。 通过 LocalSiloPreferenceMargin
属性(选项的一部分)控制此项。
此外,在线自适应算法通过将信号转换为多项式衰减过程来避免快速信号下降,从而提供平滑效果。 这对于 CPU 使用率尤其重要,有助于避免孤岛上的资源饱和,尤其是新加入的资源。
此算法基于 基于资源的放置与协作双模式 Kalman 筛选。
通过将ResourceOptimizedPlacementAttribute添加到粒度类来配置此放置策略。
选择放置策略
选择适当的粒度放置策略(超出默认值 Orleans )需要监视和评估。 选择应基于应用的大小和复杂性、工作负荷特征和部署环境。
随机放置依赖于大数法则,因此对于跨许多粒(10,000 或更多)的不可预知负载来说,这通常是一个很好的默认选择。
基于激活计数的放置也具有随机性,依赖于“两次选择的力量”原则。 这是用于分布式负载均衡的常用算法,用于常用负载均衡器。 孤岛经常将运行时统计信息发布到群集中的其他孤岛,包括:
- 可用内存、总物理内存和内存使用量。
- CPU 使用率。
- 总激活计数和最近活动激活计数。
- 过去几秒钟内的激活内容滑动窗口,有时称为激活工作集。
从这些统计信息中,目前仅使用激活计数来确定特定筒仓的负载。
最终,尝试不同的策略并监视性能指标,以确定最佳拟合度。 选择适当的粒度放置策略可优化应用的性能、可伸缩性和成本效益 Orleans 。
配置默认放置策略
Orleans 除非默认值已被覆盖,否则使用随机放置。 在配置期间通过注册 PlacementStrategy 的实现来覆盖默认放置策略:
siloBuilder.ConfigureServices(services =>
services.AddSingleton<PlacementStrategy, MyPlacementStrategy>());
配置粒度放置策略
通过将适当的属性添加到粒度类来配置粒度类型的放置策略。 相关属性在上述 放置策略 部分指定。
自定义放置策略示例
首先,定义实现接口的 IPlacementDirector 类,需要单个方法。 在此示例中,假设定义了一个函数,该函数GetSiloNumber
返回一个筒仓编号,给定即将创建的粮食的Guid。
public class SamplePlacementStrategyFixedSiloDirector : IPlacementDirector
{
public Task<SiloAddress> OnAddActivation(
PlacementStrategy strategy,
PlacementTarget target,
IPlacementContext context)
{
var silos = context.GetCompatibleSilos(target).OrderBy(s => s).ToArray();
int silo = GetSiloNumber(target.GrainIdentity.PrimaryKey, silos.Length);
return Task.FromResult(silos[silo]);
}
}
然后,定义两个类,以允许向策略分配粒度类:
[Serializable]
public sealed class SamplePlacementStrategy : PlacementStrategy
{
}
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class SamplePlacementStrategyAttribute : PlacementAttribute
{
public SamplePlacementStrategyAttribute() :
base(new SamplePlacementStrategy())
{
}
}
然后,用属性标记任何意图使用此策略的粒度类。
[SamplePlacementStrategy]
public class MyGrain : Grain, IMyGrain
{
// ...
}
最后,在构建ISiloHost
时注册策略:
private static async Task<ISiloHost> StartSilo()
{
var builder = new HostBuilder(c =>
{
// normal configuration methods omitted for brevity
c.ConfigureServices(ConfigureServices);
});
var host = builder.Build();
await host.StartAsync();
return host;
}
private static void ConfigureServices(IServiceCollection services)
{
services.AddPlacementDirector<SamplePlacementStrategy, SamplePlacementStrategyFixedSiloDirector>();
}
若需查看进一步使用放置上下文的第二个简单示例,请参阅PreferLocalPlacementDirector
在Orleans源存储库中的位置。