第 11 章 Red Hat build of OptaPlanner with Java solvers:一个云平衡快速启动指南


本指南指导您完成创建带有 OptaPlanner 约束(AI)的 Java solvers 的过程。

11.1. Java solvers 入门:云供应商示例

例如,使用 Java 代码开发基本红帽构建的 OptaPlanner 可解决器。

假设您的公司拥有多个云计算机,需要在这些计算机上运行多个进程。您必须将每个进程分配到计算机。

必须满足以下硬限制:

  • 每个计算机都必须能够处理进程总和的最低硬件要求:

    • CPU 容量 :计算机的 CPU 能力必须至少是分配给该计算机的进程所需的 CPU 电源总和。
    • 内存容量 :计算机的 RAM 内存必须至少是分配给该计算机的进程所需的 RAM 内存总和。
    • 网络容量 :计算机的网络带宽必须至少是分配给该计算机的进程所需的网络带宽总和。

应优化以下软限制:

  • 每个分配了一个或多个进程的计算机都会产生维护成本,每个计算机会修复它。

    • 成本 :最小化维护成本。

此问题是 bin 打包 的形式。在以下简化的示例中,我们使用简单算法为两台限制(CPU 和 RAM)为两台计算机分配四个进程:

此处使用的简单算法是第一个 Fit Decreasing 算法,它会首先 分配较大的进程,并将较小的进程分配给剩余的空间。看不到,它不是最佳的,因为它没有足够的空间来分配黄色进程 D

OptaPlanner 使用额外的智能算法找到更最佳的解决方案。它还扩展:在数据(更多进程、更多计算机)和限制(更多硬件要求、其他限制)中。

以下概述适用于本例,以及 第 4.7 节 “机器重新分配(Google ROADEF 2012)” 中描述的更多限制的高级实现:

Expand
表 11.1. 云平衡问题大小
问题大小计算机Process搜索空间

2computers-6processes

2

6

64

3computers-9processes

3

9

10^4

4computers-012processes

4

12

10^7

100computers-300processes

100

300

10^600

200computers-600processes

200

600

10^1380

400computers-1200processes

400

1200

10^3122

800computers-2400processes

800

2400

10^6967

11.1.1. 域模型设计

使用 域模型 有助于确定哪些类正在规划实体及其属性计划变量。它还有助于简化约束、提高性能并提高未来需求的灵活性。

11.1.1.1. 设计域模型

要创建域模型,请定义代表问题的输入数据的所有对象。在本例中,对象是进程和计算机。

域模型中的单独的对象必须代表一组问题,其中包含输入数据和解决方案。在本例中,此对象包含计算机列表和进程列表。每个进程分配到一个计算机;计算机之间的进程分布是解决方案。

流程

  1. 绘制域模型的类图。
  2. 将其规范化以删除重复的数据。
  3. 为每个类编写 一些示例实例。示例实例是与规划目的相关的实体属性。

    • 计算机 :代表具有特定硬件和维护成本的计算机。

      在本例中,计算机 类的示例实例为 cpuPower内存networkBandwidth成本 为。

    • 进程 :代表具有需求的进程。需要由 Planner 分配给 计算机

      Process 实例示例 是必需的CpuPowerRequireMemoryrequiredNetworkBandwidth

    • CloudBalance :代表计算机之间进程分布。包含特定 数据集 的每个 计算机 和流程。

      对于代表完整数据集和解决方案的对象,必须存在包含 分数 的示例实例。OptaPlanner 可以计算和比较不同解决方案的分数;具有最高分数的解决方案是最佳解决方案。因此,CloudBalance 的示例实例 分数 为。

  4. 在规划过程中确定哪些关系(或字段)变化:

    • 规划实体 : OptaPlanner 的类(或类)在技术期间可能会改变。在本例中,它是类 进程,因为我们可以将进程移到不同的计算机。

      • 代表 OptaPlanner 无法更改的输入数据的类被称为 问题事实
    • 规划变量 :计划实体类的属性(或属性),这些类在技术期间发生了变化。在本例中,这是类 进程 的属性 计算机
    • 规划解决方案 :代表问题解决方案的类。此类必须代表完整的数据集,并包含所有规划实体。在本例中,是类 CloudBalance

在以下 UML 类图中,OptaPlanner 概念已被注解:

您可以在 examples/sources/src/main/java/org/optaplanner/examples/cloudbalancing/domain 目录中找到本例的类定义。

11.1.1.2. 计算机 类

Computer 类是一种 Java 对象,它存储数据,有时被称为 POJO (旧 Java 对象)。通常,您将拥有许多带有输入数据的类。

例 11.1. CloudComputer.java

public class CloudComputer ... {

    private int cpuPower;
    private int memory;
    private int networkBandwidth;
    private int cost;

    ... // getters
}
Copy to Clipboard Toggle word wrap

11.1.1.3. 进程 类

Process 类是参与期间修改的类。

我们需要告诉 OptaPlanner,它可以更改属性 计算机。为此,可使用 @PlanningEntity 注解类,并使用 @PlanningVariable 注解 getComputer () getter。

课程,属性 计算机还需要 设置者,因此 OptaPlanner 可在公司期间更改它。

例 11.2. CloudProcess.java

@PlanningEntity(...)
public class CloudProcess ... {

    private int requiredCpuPower;
    private int requiredMemory;
    private int requiredNetworkBandwidth;

    private CloudComputer computer;

    ... // getters

    @PlanningVariable(valueRangeProviderRefs = {"computerRange"})
    public CloudComputer getComputer() {
        return computer;
    }

    public void setComputer(CloudComputer computer) {
        computer = computer;
    }

    // ************************************************************************
    // Complex methods
    // ************************************************************************

    ...

}
Copy to Clipboard Toggle word wrap

OptaPlanner 需要知道它可以选择哪些值分配给属性 计算机。这些值从规划解决方案上的方法 CloudBalance.getComputerList () 检索,后者返回当前数据集中的所有计算机的列表。

CloudProcess.getComputer () 上的 @PlanningVariable 'svalueRangeProviderRefs 参数需要与 CloudBalance.getComputerList () 上的 @ValueRangeProvider 'sid 匹配。

注意

您还可以在字段而不是 getters 上使用注解。

11.1.1.4. CloudBalance 类

CloudBalance 类具有 @PlanningSolution 注释。

此类包含所有计算机和进程的列表。它代表了规划问题,以及(如果被初始化)规划解决方案。

CloudBalance 类有以下关键属性:

  • 它包含 OptaPlanner 可能会更改的进程集合。我们使用 @PlanningEntityCollectionProperty 注解 getter getProcessList (),以便 OptaPlanner 能够检索它可以更改的进程。要保存解决方案,OptaPlanner 使用更改的进程列表初始化类的新实例。

    1. 它还有一个 @PlanningScore 注解的属性 分数,这是该解决方案处于当前状态的 分数。OptaPlanner 会在计算解决方案实例的 Score 时自动更新它,因此此属性需要 setter。
    2. 特别是使用 Drools 进行分数计算,属性 computerList 需要用 @ProblemFactCollectionProperty 注解,以便 OptaPlanner 能够检索计算机列表(虚拟化事实)并使其提供给决策引擎。

例 11.3. CloudBalance.java

@PlanningSolution
public class CloudBalance ... {

    private List<CloudComputer> computerList;

    private List<CloudProcess> processList;

    private HardSoftScore score;

    @ValueRangeProvider(id = "computerRange")
    @ProblemFactCollectionProperty
    public List<CloudComputer> getComputerList() {
        return computerList;
    }

    @PlanningEntityCollectionProperty
    public List<CloudProcess> getProcessList() {
        return processList;
    }

    @PlanningScore
    public HardSoftScore getScore() {
        return score;
    }

    public void setScore(HardSoftScore score) {
        this.score = score;
    }

    ...
}
Copy to Clipboard Toggle word wrap

11.1.2. 运行 Cloud balancing Hello World 应用程序

您可以运行示例 Hello World 应用程序来演示解析器。

流程

  1. 下载并配置您首选的 IDE 中的示例。有关在 IDE 中下载和配置示例的步骤,请参考 第 5.2 节 “在 IDE 中运行红帽构建的 OptaPlanner 示例(IntelliJ、Ecli 或 Netbeans)”
  2. 使用以下主类创建运行配置: org.optaplanner.examples.cloudbalancing.app.CloudBalancingHelloWorld

    默认情况下,Cloud Balancing Hello World 配置为运行 120 秒。

结果

应用程序执行以下代码:

例 11.4. CloudBalancingHelloWorld.java

public class CloudBalancingHelloWorld {

    public static void main(String[] args) {
        // Build the Solver
        SolverFactory<CloudBalance> solverFactory = SolverFactory.createFromXmlResource("org/optaplanner/examples/cloudbalancing/solver/cloudBalancingSolverConfig.xml");
        Solver<CloudBalance> solver = solverFactory.buildSolver();

        // Load a problem with 400 computers and 1200 processes
        CloudBalance unsolvedCloudBalance = new CloudBalancingGenerator().createCloudBalance(400, 1200);

        // Solve the problem
        CloudBalance solvedCloudBalance = solver.solve(unsolvedCloudBalance);

        // Display the result
        System.out.println("\nSolved cloudBalance with 400 computers and 1200 processes:\n" + toDisplayString(solvedCloudBalance));
    }

    ...
}
Copy to Clipboard Toggle word wrap

代码示例执行以下操作:

  1. 根据 解析器 配置构建 Solver (本例中为 XML 文件 cloudBalancingSolverConfig.xml )。

    构建 Solver 是此流程中最复杂的部分。如需了解更多详细信息,请参阅 第 11.1.3 节 “solver 配置”

            SolverFactory<CloudBalance> solverFactory = SolverFactory.createFromXmlResource(
                    "org/optaplanner/examples/cloudbalancing/solver/cloudBalancingSolverConfig.xml");
            Solver solver<CloudBalance> = solverFactory.buildSolver();
    Copy to Clipboard Toggle word wrap
  2. 加载问题。

    CloudBalancingGenerator 生成一个随机问题:您将替换为加载实际问题的类,例如从数据库中。

            CloudBalance unsolvedCloudBalance = new CloudBalancingGenerator().createCloudBalance(400, 1200);
    Copy to Clipboard Toggle word wrap
  3. 解决问题。

            CloudBalance solvedCloudBalance = solver.solve(unsolvedCloudBalance);
    Copy to Clipboard Toggle word wrap
  4. 显示结果。

            System.out.println("\nSolved cloudBalance with 400 computers and 1200 processes:\n"
                    + toDisplayString(solvedCloudBalance));
    Copy to Clipboard Toggle word wrap

11.1.3. solver 配置

solver 配置文件决定了参与过程的工作方式;它被视为代码的一部分。该文件命名为 example /sources/src/main/resources/org/optaplanner/examples/cloudbalancing/solver/cloudBalancingSolverConfig.xml

例 11.5. cloudBalancingSolverConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<solver>
  <!-- Domain model configuration -->
  <scanAnnotatedClasses/>

  <!-- Score configuration -->
  <scoreDirectorFactory>
    <easyScoreCalculatorClass>org.optaplanner.examples.cloudbalancing.optional.score.CloudBalancingEasyScoreCalculator</easyScoreCalculatorClass>
    <!--<scoreDrl>org/optaplanner/examples/cloudbalancing/solver/cloudBalancingScoreRules.drl</scoreDrl>-->
  </scoreDirectorFactory>

  <!-- Optimization algorithms configuration -->
  <termination>
    <secondsSpentLimit>30</secondsSpentLimit>
  </termination>
</solver>
Copy to Clipboard Toggle word wrap

这个 resolver 配置由三个部分组成:

  1. 域模型配置 :OptaPlanner 可以更改什么?

    我们需要让 OptaPlanner 了解我们的域类。在此配置中,它将自动扫描类路径中的所有类(对于 @PlanningEntity@PlanningSolution 注解):

      <scanAnnotatedClasses/>
    Copy to Clipboard Toggle word wrap
  2. score configuration:OptaPlanner 应优化规划变量?我们的目标是什么?

    由于我们存在硬和软限制,因此我们使用一个 HardSoftScore。但我们需要根据我们的业务需求告知 OptaPlanner 如何计算分数。此外,我们将查看两种替代方法来计算分数:使用基本的 Java 实施和使用 droolL。

      <scoreDirectorFactory>
        <easyScoreCalculatorClass>org.optaplanner.examples.cloudbalancing.optional.score.CloudBalancingEasyScoreCalculator</easyScoreCalculatorClass>
        <!--<scoreDrl>org/optaplanner/examples/cloudbalancing/solver/cloudBalancingScoreRules.drl</scoreDrl>-->
      </scoreDirectorFactory>
    Copy to Clipboard Toggle word wrap
  3. 优化算法配置OptaPlanner 应如何优化它?在这种情况下,我们使用默认的优化算法(因为没有配置显式优化算法) 30 秒:

      <termination>
        <secondsSpentLimit>30</secondsSpentLimit>
      </termination>
    Copy to Clipboard Toggle word wrap

    OptaPlanner 应该会得到很好的结果(如果使用实时规划功能,则结果少于 15 毫秒),但其结果越好,则结果越好。高级用例可能会使用与硬时间限制不同的终止标准。

    默认算法将容易使用人工规划器和大多数内部实施。您可以使用高级基准测试程序功能来更好地获得更好的结果。

11.1.4. 分数配置

OptaPlanner 将搜索具有最高 分数的解决方案这个示例使用 Hard SoftScore,这意味着 OptaPlanner 将查找没有硬约束(需要满足硬件要求)的解决方案,并尽量少的软约束问题(降低维护成本)。

您可以使用普通 Java、Jrools 或 OptaPlanner ConstraintStream API 来定义限制。有关 ConstraintStream API 的详情,请参考 第 10.3 节 “定义约束并计算分数”

11.1.4.1. 使用 Java 配置分数计算

定义分数功能的一种方法是以普通 Java 实施接口 EasyScoreCalculator

流程

  1. 在 Cloud BalancingSolverConfig.xml 文件中,添加或取消注释设置:

      <scoreDirectorFactory>
        <easyScoreCalculatorClass>org.optaplanner.examples.cloudbalancing.optional.score.CloudBalancingEasyScoreCalculator</easyScoreCalculatorClass>
      </scoreDirectorFactory>
    Copy to Clipboard Toggle word wrap
  2. 实施 compute Score (Solution) 方法,以返回 Hard SoftScore 实例。

    例 11.6. CloudBalancingEasyScoreCalculator.java

    public class CloudBalancingEasyScoreCalculator implements EasyScoreCalculator<CloudBalance> {
    
        /**
         * A very simple implementation. The double loop can easily be removed by using Maps as shown in
         * {@link CloudBalancingMapBasedEasyScoreCalculator#calculateScore(CloudBalance)}.
         */
        public HardSoftScore calculateScore(CloudBalance cloudBalance) {
            int hardScore = 0;
            int softScore = 0;
            for (CloudComputer computer : cloudBalance.getComputerList()) {
                int cpuPowerUsage = 0;
                int memoryUsage = 0;
                int networkBandwidthUsage = 0;
                boolean used = false;
    
                // Calculate usage
                for (CloudProcess process : cloudBalance.getProcessList()) {
                    if (computer.equals(process.getComputer())) {
                        cpuPowerUsage += process.getRequiredCpuPower();
                        memoryUsage += process.getRequiredMemory();
                        networkBandwidthUsage += process.getRequiredNetworkBandwidth();
                        used = true;
                    }
                }
    
                // Hard constraints
                int cpuPowerAvailable = computer.getCpuPower() - cpuPowerUsage;
                if (cpuPowerAvailable < 0) {
                    hardScore += cpuPowerAvailable;
                }
                int memoryAvailable = computer.getMemory() - memoryUsage;
                if (memoryAvailable < 0) {
                    hardScore += memoryAvailable;
                }
                int networkBandwidthAvailable = computer.getNetworkBandwidth() - networkBandwidthUsage;
                if (networkBandwidthAvailable < 0) {
                    hardScore += networkBandwidthAvailable;
                }
    
                // Soft constraints
                if (used) {
                    softScore -= computer.getCost();
                }
            }
            return HardSoftScore.valueOf(hardScore, softScore);
        }
    
    }
    Copy to Clipboard Toggle word wrap

即使我们优化了上述代码以使用 Maps 来迭代 processList 一次,它仍然会很慢,因为它不会进行增量分数计算。

要修复这个问题,可以使用增量 Java 分数计算或 drools 分数计算。本指南中不涵盖增量 Java 分数计算。

11.1.4.2. 使用 drools 配置分数计算

您可以使用 dols 规则语言(DRL)来定义限制。dols 分数计算使用增量计算,每个分数约束都会写成一个或多个分数规则。

通过使用决策引擎进行分数计算,您可以与其他 dols 技术集成,如分区表(基于 XLS 或 Web)、Business Central 和其他支持的功能。

流程

  1. 在 classpath 中添加 scoreDrl 资源,以使用决策引擎作为分数功能。在 Cloud BalancingSolverConfig.xml 文件中,添加或取消注释设置:

      <scoreDirectorFactory>
        <scoreDrl>org/optaplanner/examples/cloudbalancing/solver/cloudBalancingScoreRules.drl</scoreDrl>
      </scoreDirectorFactory>
    Copy to Clipboard Toggle word wrap
  2. 创建硬限制。这些限制可确保所有计算机有足够的 CPU、RAM 和网络带宽来支持其所有进程:

    例 11.7. cloudBalancingScoreRules.drl - Hard Constraints

    ...
    
    import org.optaplanner.examples.cloudbalancing.domain.CloudBalance;
    import org.optaplanner.examples.cloudbalancing.domain.CloudComputer;
    import org.optaplanner.examples.cloudbalancing.domain.CloudProcess;
    
    global HardSoftScoreHolder scoreHolder;
    
    // ############################################################################
    // Hard constraints
    // ############################################################################
    
    rule "requiredCpuPowerTotal"
        when
            $computer : CloudComputer($cpuPower : cpuPower)
            accumulate(
                CloudProcess(
                    computer == $computer,
                    $requiredCpuPower : requiredCpuPower);
                $requiredCpuPowerTotal : sum($requiredCpuPower);
                $requiredCpuPowerTotal > $cpuPower
            )
        then
            scoreHolder.addHardConstraintMatch(kcontext, $cpuPower - $requiredCpuPowerTotal);
    end
    
    rule "requiredMemoryTotal"
        ...
    end
    
    rule "requiredNetworkBandwidthTotal"
        ...
    end
    Copy to Clipboard Toggle word wrap
  3. 创建软约束。此约束可最小化维护成本。只有在满足硬限制时才会应用它:

    例 11.8. cloudBalancingScoreRules.drl - Soft Constraints

    // ############################################################################
    // Soft constraints
    // ############################################################################
    
    rule "computerCost"
        when
            $computer : CloudComputer($cost : cost)
            exists CloudProcess(computer == $computer)
        then
            scoreHolder.addSoftConstraintMatch(kcontext, - $cost);
    end
    Copy to Clipboard Toggle word wrap

11.1.5. 进一步开发问题

现在,本示例可以正常工作,您可以尝试进一步开发它。例如,您可以增强域模型并添加额外的限制,例如:

  • 每个进程都 属于一个 服务。计算机可能会崩溃,因此运行同一服务的进程应该(或必须)分配给不同的计算机。
  • 每个计算机 都位于一个构建 。构建可能会刻录,因此应(或必须)将同一服务的进程分配给不同构建中的计算机。
返回顶部
Red Hat logoGithubredditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

通过我们的产品和服务,以及可以信赖的内容,帮助红帽用户创新并实现他们的目标。 了解我们当前的更新.

让开源更具包容性

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。欲了解更多详情,请参阅红帽博客.

關於紅帽

我们提供强化的解决方案,使企业能够更轻松地跨平台和环境(从核心数据中心到网络边缘)工作。

Theme

© 2025 Red Hat