粒度放置

Orleans 确保进行粒度调用时,群集中某些服务器上的内存中提供了该粒度的实例来处理请求。 如果微粒当前在群集中未处于活动状态,Orleans 会选择一个服务器来激活微粒。 这称为粒度放置。 放置也是负载均衡的一种方式:即使繁忙粒度的放置也有助于均衡整个群集中的工作负载。

Orleans 中的放置过程是完全可配置的:开发人员可以从一组现成的放置策略(例如随机、首选本地和基于负载)或自定义逻辑中进行选择。 这允许在决定创建粒度的位置方面具有完全的灵活性。 例如,可将粒度放置在与它们需要操作的资源相互靠近的服务器上,或者放置在与它们通信的其他粒度靠近的服务器上。 默认情况下,Orleans 将选取随机兼容的服务器。

Orleans 使用的放置策略可以全局或根据各个 grain 类进行配置。

随机放置

从群集中的兼容服务器随机选择服务器。 通过向粒度添加 RandomPlacementAttribute 来配置此放置策略。

本地放置

如果本地服务器兼容,请选择本地服务器,否则选择一个随机服务器。 通过向粒度添加 PreferLocalPlacementAttribute 来配置此放置策略。

基于哈希的放置

将 grain ID 哈希为非负整数,并用兼容服务器的数量对其进行取模。 从按服务器地址排序的兼容服务器列表中选择相应的服务器。 请注意,这不能保证在群集成员身份发生变化时保持稳定。 具体而言,添加、删除或重启服务器可以更改为给定粒度 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
{
    // ...
}

最后,在生成 SiloHost 时注册策略:

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>();
}

有关显示进一步使用放置上下文的第二个简单示例,请参阅 Orleans 源存储库中的 PreferLocalPlacementDirector