简单案例

一个简单的仿真案例包括一下基本步骤:

  • 初始化CloudSim
  • 创建数据中心代理
  • 创建数据中心、创建主机列表
  • 在主机中放置虚拟机
  • 创建云任务Cloudlet
  • 提交划分的虚拟机列表给代理
  • 提交云任务
  • 仿真开始
  • 获得仿真结果并输出
public class ReducedExample {
    public static void main(String[] args) {
        // 设置日志级别
        //Log.setLevel(ch.qos.logback.classic.Level.WARN);

        // 1.初始化CloudSim
        CloudSim cloudsim = new CloudSim();

        // 2.创建将代表云用户(客户)行事的 Broker。
        DatacenterBroker broker0 = new DatacenterBrokerSimple(cloudsim);

        // 3.使用特定的 CPU 内核 (PE) 列表创建一个主机.
        List<Host> hostList = new ArrayList<>(1);
        List<Pe> hostPes = new ArrayList<>(1);
        // Uses a PeProvisionerSimple by default to provision PEs for VMs
        hostPes.add(new PeSimple(20000));
        final long ram = 10000; //in Megabytes
        final long storage = 100000; //in Megabytes
        final long bw = 100000; //in Megabits/s
        // Uses ResourceProvisionerSimple by default for RAM and BW provisioning
        // Uses VmSchedulerSpaceShared by default for VM scheduling
        Host host0 = new HostSimple(ram, bw, storage, hostPes);
        hostList.add(host0);

        // 4.使用主机列表创建一个数据中心
        // Uses a VmAllocationPolicySimple by default to allocate VMs
        Datacenter dc0 = new DatacenterSimple(cloudsim, hostList);

        // 5.创建一个 Vm 来运行应用程序 (Cloudlets)。
        List<Vm> vmList = new ArrayList<>(1);
        // Uses a CloudletSchedulerTimeShared by default to schedule Cloudlets
        Vm vm0 = new VmSimple(1000, 1);
        vm0.setRam(1000).setBw(1000).setSize(1000);
        vmList.add(vm0);

        // 6.创建两个要在 Vm 内运行的应用程序(Cloudlet)。
        List<Cloudlet> cloudlets = new ArrayList<>(1);
        // UtilizationModel defining the Cloudlets use only 50% of any resource all the time
        UtilizationModelDynamic utilizationModel = new UtilizationModelDynamic(0.5);
        Cloudlet cloudlet0 = new CloudletSimple(10000, 1, utilizationModel);
        Cloudlet cloudlet1 = new CloudletSimple(10000, 1, utilizationModel);
        cloudlets.add(cloudlet0);
        cloudlets.add(cloudlet1);

        // 7.请求代理创建 Vms 和 Cloudlets。
        // 它将选择 Host 来放置每个 Vm 和一个 Vm 来运行每个 Cloudlet。
        broker0.submitVmList(vmList);
        broker0.submitCloudletList(cloudlets);
        
        // 8.启动模拟并等待所有 cloudlets 执行,当没有更多事件要处理时会自动停止.
        cloudsim.start();
        
		// 9.模拟结束时打印结果
        new CloudletsTableBuilder(broker0.getCloudletFinishedList()).build();
    }
}

调度策略

虚拟机调度策略

虚拟机调度策略也称之为虚拟机放置策略,即如何将虚拟机放置在数据中心的主机(host)中。

自带的策略:

  • VmAllocationPolicyFirstFit
  • VmAllocationPolicyBestFit
  • VmAllocationPolicyRandom
  • VmAllocationPolicyRoundRobin
  • VmAllocationPolicyWorstFit
  • VmAllocationPolicySimple

例子:

new DatacenterSimple(simulation, hostList,  new VmAllocationPolicySimple())

自定义策略:

如何实现自定义的放置策略?只需要继承VmAllocationPolicyAbstract类,重写defaultFindHostForVm方法。

例子:

/**
 * @Author: mhh
 * @Date: 2021/8/10
 * @Description: 自定义虚拟机放置算法(随机放置)
 */
public class MyVmAllocationPolicy extends VmAllocationPolicyAbstract {

    private final ContinuousDistribution random  = new UniformDistr();;

    @Override
    protected Optional<Host> defaultFindHostForVm(Vm vm) {
        final List<Host> hostList = this.getHostList();
        // 索引“i”不是用来获取 Host 的位置,而是记录尝试寻找合适主机Host的次数。
        for (int i = 0; i < hostList.size(); i++){
            final int randomIndex = (int)(random.sample() * hostList.size());
            final Host host = hostList.get(randomIndex);
            if(host.isSuitableForVm(vm)){
                return Optional.of(host);
            }
        }
        return Optional.empty();
    }
}

任务调度策略

任务调度策略是指将任务(Cloudlet)委派给某个虚拟机Vm执行。

默认策略

  • DatacenterBrokerSimple:顺序调度,把一组任务顺序分配给一组虚拟机。
  • DatacenterBrokerBestFit:选择能够运行给定 Cloudlet 的 PE 数量最少的 VM。
  • DatacenterBrokerFirstFit:选择能够运行给定 Cloudlet 的 PE 数量最少的第一个VM
  • DatacenterBrokerHeuristic:使用一些启发式方法,来获得提交的 cloudlets 和 Vm 之间的次优映射

自定义策略

有几种方式,简单介绍两种。

方式一

在将任务( Cloudlets)提交给代理(DatacenterBroker)前,手动将任务绑定到指定的虚拟机。

例如:自定义实现顺序分配策略

// 6.创建云任务
cloudletList = createCloudlets();
// 使用自定义的任务分配策略
bindCloudletToVmsSimple();
// 7.提交划分的虚拟机列表到代理
broker0.submitVmList(vmList);
// 8.提交云任务
broker0.submitCloudletList(cloudletList);
/**
 * 顺序分配策略:把一组任务顺序分配给一组虚拟机
 */
private void bindCloudletToVmsSimple(){
    int vmNumber = vmList.size();
    int cloudletNumber = cloudletList.size();
    int index = 0;
    for (Cloudlet cloudlet : cloudletList) {
        cloudlet.setVm(vmList.get(index));
        index = (index + 1) % vmNumber;
    }
}

方式二

继承DatacenterBrokerSimple类,重写defaultVmMapper方法。

例如:

public class DatacenterBrokerBestFit extends DatacenterBrokerSimple {

    public DatacenterBrokerBestFit(final CloudSim simulation) {
        super(simulation);
    }

    /**
     * 选择能够运行给定 Cloudlet 的 PE 数量最少的 VM
     * 
     */
    @Override
    public Vm defaultVmMapper(final Cloudlet cloudlet) {
        if (cloudlet.isBoundToVm()) {
            return cloudlet.getVm();
        }

        final Vm mappedVm = getVmCreatedList()
            .stream()
            .filter(vm -> vm.getExpectedFreePesNumber() >= cloudlet.getNumberOfPes())
            .min(Comparator.comparingLong(Vm::getExpectedFreePesNumber))
            .orElse(Vm.NULL);

        if (mappedVm == Vm.NULL) {
            LOGGER.warn("{}: {}: {} (PEs: {}) couldn't be mapped to any suitable VM.",
                getSimulation().clockStr(), getName(), cloudlet, cloudlet.getNumberOfPes());
        } else {
            LOGGER.trace("{}: {}: {} (PEs: {}) mapped to {} (available PEs: {}, tot PEs: {})",
                getSimulation().clockStr(), getName(), cloudlet, cloudlet.getNumberOfPes(), mappedVm,
                mappedVm.getExpectedFreePesNumber(), mappedVm.getFreePesNumber());
        }

        return mappedVm;
    }
}

CPU调度策略

CloudSim Plus在两个层面上调度CPU资源:HostVM

Host层面

在Host层面,host将处理单元(Processor Element,PE)分片划分给运行于host上的各个VM。由于资源被各个VM共享,所以这个调度器称为VmScheduler。在一个host实例化后,必须设置一个VmScheduler调度器。

例如:Host层面的CPU资源调度。

Host host = new HostSimple(ram, bw, storage, peList);
host.setRamProvisioner(new ResourceProvisionerSimple())
    .setBwProvisioner(new ResourceProvisionerSimple())
    .setVmScheduler(new VmSchedulerTimeShared());

VM层面

在VM层面,每个虚拟机将从host那里分配到的资源划分给在虚拟机中运行的各个Cloudlet。由于资源被各个Cloudlets共享,所以这个调度器成称为CloudletScheduler。在一个VM创建后,必须设置一个CloudletScheduler调度器。

例如:Vm层面的CPU资源调度。

Vm vm = new VmSimple(i, 1000, VM_PES)
            .setRam(512).setBw(1000).setSize(10000)
            .setCloudletScheduler(new CloudletSchedulerTimeShared());

默认调度策略

在上述两个层面,有两个默认的调度策略可用:

  • xSpaceShared:(x代表VmScheduler或者CloudletScheduler),Cloudlet或VM所需的PE将被独有地安排。这意味着如果Cloudlet或VM数量大于可用的PE数量,后面到达的Cloudlet或VM将在队列中等待,直到有足够的空闲资源。
  • xTimeShared:运行中的Cloudlet或VM分时享有可用的PE,所有Cloudlet或VM同时运行。

上述Cloudlet和VM有关策略可以以任何组合形式搭配使用。例如,你可以使用VmSchedulerTimeSharedCloudletSchedulerSpaceShared,或者VmSchedulerTimeSharedCloudletSchedulerTimeShared。甚至一个运行了多个虚拟机的Host有多种CloudletScheduler或者是一个运行了多个Host的Datacenter有多种VmScheduler也是可能的。

自定义调度策略

如果要自定义调度策略,可以通过继承一个VmScheduler 或者CloudletScheduler类来实现。

参考

云计算仿真工具CloudSim Plus常见问题总结