CloudSim有关NetworkDatacenter的学习
前言
本人最近在学习CloudSim,本来以为CloudSim用来做任务调度就是全部的功能了,但是发现CloudSim对于云环境的仿真已经达到了很高的程度,尤其是其中关于工作流的模拟,可以很好的帮助有兴趣研究这方面科研的同学制作细粒度与粗粒度相关的计算卸载实验,但是CloudSim里面唯一给了一个TestExample的例子对于如何创建属于自己的workflow介绍十分笼统,于是我自己研究了一下例子里的实验是如何运行的,在这边做一个笼统的介绍,希望能抛砖引玉,帮助到各位有这方面研究需求的同学。
提示:仅供参考,希望大佬轻喷,有不同意见的小伙伴欢迎一起交流~
一、CloudSim简介
CloudSim是墨尔本大学开发的一款云仿真器。
CloudSim提供了虚拟化引擎,旨在数据中心节点上帮助建立和管理多重的、独立的、协同的的虚拟化服务;在对虚拟化服务分配处理核心时能够在时间共享和空间共享之间灵活切换。
CloudSim平台有助于加快云计算的算法、方法和规范的发展。CloudSim的组件工具均为开源的。CloudSim的软件结构框架和体系结构组件包括SimJava、GridSim、CloudSim、UserCode四个层次。
云计算与网格计算的一个显著区别是云计算采用了成熟的虚拟化技术,将数据中心的资源虚拟化为资源池,打包对外向用户提供服务,Cloudsim体现了此特点,扩展部分实现了一系列接口,提供基于数据中心的虚拟化技术、虚拟化云的建模和仿真功能。通常,数据中心的一台主机的资源可以根据用户的需求映射到多台虚拟机上,因此,虚拟机之间存在对主机资源的竞争关系。Cloudsim提供了资源的监测、主机到虚拟机的映射功能。
(摘自百度百科)
二、关于NetworkDataCenter的TestExample
1.TestExample执行函数代码
代码如下:
public static void main(String[] args) {
Log.printLine("Starting CloudSimExample1...");
try {
int num_user = 1; // number of cloud users
Calendar calendar = Calendar.getInstance();
boolean trace_flag = false; // mean trace events
// Initialize the CloudSim library
CloudSim.init(num_user, calendar, trace_flag);
// Second step: Create Datacenters
// Datacenters are the resource providers in CloudSim. We need at
// list one of them to run a CloudSim simulation
NetworkDatacenter datacenter0 = createDatacenter("Datacenter_0");
// Third step: Create Broker
NetDatacenterBroker broker = createBroker();
broker.setLinkDC(datacenter0);
// broker.setLinkDC(datacenter0);
// Fifth step: Create one Cloudlet
vmlist = new ArrayList<NetworkVm>();
// submit vm list to the broker
broker.submitVmList(vmlist);
// Sixth step: Starts the simulation
CloudSim.startSimulation();
CloudSim.stopSimulation();
// Final step: Print results when simulation is over
List<Cloudlet> newList = broker.getCloudletReceivedList();
printCloudletList(newList);
System.out.println("numberofcloudlet " + newList.size() + " Cached "
+ NetDatacenterBroker.cachedcloudlet + " Data transfered "
+ NetworkConstants.totaldatatransfer);
Log.printLine("CloudSimExample1 finished!");
} catch (Exception e) {
e.printStackTrace();
Log.printLine("Unwanted errors happen");
}
}
从上面执行函数与CloudSim的Example1比较来看,主函数仅仅初始化了Datacenter和Broker两个对象,VmList仅仅是作为一个空列表被提交到broker中,这和Example1中的有很大的差别。这里之所以这样做的原因是因为,在真实环境中,创建虚拟机与进行云任务调度的过程是对用户不可见的,因此在模拟环境下,TestExample将创建云任务和创建Vm以及对云任务进行分配的过程封装到NetDatacenterBroker中。通过SetLinkDC()函数,将broker与datacenter进行绑定,也就是说broker将“租用”此数据中心来提供云服务(创建虚拟机)。
broker.setLinkDC(datacenter0);
2.NetDatacenterBroker创建Vm过程
由于NetDatacenterBroker包含变量十分丰富,在这里仅讨论有关TestExample的Vm创建过程。
首先,当程序开始执行时,通过ProcessEvent,获取当前执行时间的标签,(这里我一直没搞懂究竟是为什么能够执行到NextCycle的。。。。)而后根据标签情况进行执行,而这里必然会执行的就是NextCycle这个标签下的程序码。
代码如下:
public void processEvent(SimEvent ev) {
switch (ev.getTag()) {
// Resource characteristics request
case CloudSimTags.RESOURCE_CHARACTERISTICS_REQUEST:
processResourceCharacteristicsRequest(ev);
break;
// Resource characteristics answer
case CloudSimTags.RESOURCE_CHARACTERISTICS:
processResourceCharacteristics(ev);
break;
// VM Creation answer
// A finished cloudlet returned
case CloudSimTags.CLOUDLET_RETURN:
processCloudletReturn(ev);
break;
// if the simulation finishes
case CloudSimTags.END_OF_SIMULATION:
shutdownEntity();
break;
case CloudSimTags.NextCycle:
if (NetworkConstants.BASE) {
createVmsInDatacenterBase(linkDC.getId());
}
break;
// other unknown tags are processed by this method
default:
processOtherEvent(ev);
break;
}
}
这个里的NetworkConstrants.BASE为一个常量,默认为true,也就是这段if下面的函数是无论如何都会执行的。所以就转入执行
createVmsInDatacenterBase(linkDC.getId())
这个函数也就是这个example所定义的一系列变量,如果想编写自己定义的程序流调度,也就是从这个函数进行修改,或者根据这个函数创建自己想要的程序调度方案。
接下来我们详细看一下创建过程。
/**
* Creates virtual machines in a datacenter and submit/schedule cloudlets to them.
*
* @param datacenterId Id of the Datacenter to create the VMs
*
* @pre $none
* @post $none
*/
protected void createVmsInDatacenterBase(int datacenterId) {
// send as much vms as possible for this datacenter before trying the
// next one
//这个参数目前还不很清楚具体作用
int requestedVms = 0;
// All host will have two VMs (assumption) VM is the minimum unit
if (createvmflag) {
CreateVMs(datacenterId);
createvmflag = false;
}
// generate Application execution Requests
for (int i = 0; i < 1; i++) {
//创建工作流,构造函数内只定义了如何构造基类,其基类是AppCloudlet
//便于查看结果,这里只设定了一个工作流,程序默认是100个
this.getAppCloudletList().add(
new WorkflowApp(AppCloudlet.APP_Workflow, NetworkConstants.currentAppId, 0, 0, getId()));
NetworkConstants.currentAppId++;
}
int k = 0;
// schedule the application on VMs
for (AppCloudlet app : this.getAppCloudletList()) {
//这个列表用于分配vmid,因为workflow里的createCloudletList方法传入参数为vmidList
List<Integer> vmids = new ArrayList<Integer>();
//LinkDC在这里一直表示的是供应商所对应的数据中心(Datacenter)
int numVms = linkDC.getVmList().size();
//随机数生成,种子(seed相同的情况下,每次生成的随机数列是相同的
UniformDistr ufrnd = new UniformDistr(0, numVms, 5);
for (int i = 0; i < app.numbervm; i++) {
int vmid = (int) ufrnd.sample();
vmids.add(vmid);
}
if (vmids != null) {
if (!vmids.isEmpty()) {
//调用workflow里的创建CloudLetList函数,创建三个相关联的任务
//这里写死的东西太多了,分配的Vm是在这里面进行分配的,且任务的计算时间也是在这个函数里写死的
//与VM和数据中心主机的MIPs无关
app.createCloudletList(vmids);
for (int i = 0; i < app.numbervm; i++) {
//clist是云任务列表,用于存放定义好的cloudlet任务
//在这里,AppCloudlet代表的是一个任务组,更确切的说是一个程序
//而一个程序内部是由多个子任务组成,而子任务的执行步骤,
//就存储于AppCloudLet的clist中,其中存放的是NetworkCloudLet
//而不是一般的CloudLet。
//将任务i归于这个用户,其实也是这个代理商
app.clist.get(i).setUserId(getId());
//Receive表,用于查看运行结果
appCloudletRecieved.put(app.appID, app.numbervm);
//提交任务表,这里提交的不是Appcloudlet,而是里面的子任务
this.getCloudletSubmittedList().add(app.clist.get(i));
cloudletsSubmitted++;
// Sending cloudlet
//这里的第一个参数写的是EntityId,并且走到最后看起来应该是要发送给目的的Id,但是貌似没有什么效果,可能是在workflow
//创建cloudlet那个函数内已经写过了要给谁了,所以这里没有什么用。
sendNow( getVmsToDatacentersMap().get(this.getVmList().get(0).getId()),
CloudSimTags.CLOUDLET_SUBMIT,
app.clist.get(i));
}
System.out.println("app" + (k++));
}
}
}
setAppCloudletList(new ArrayList<AppCloudlet>());
if (NetworkConstants.iteration < 10) {
NetworkConstants.iteration++;
this.schedule(getId(), NetworkConstants.nexttime, CloudSimTags.NextCycle);
}
setVmsRequested(requestedVms);
setVmsAcks(0);
}
3.workflow创建工作流
话不多说,先上代码:
public void createCloudletList(List<Integer> vmIdList) {
long fileSize = NetworkConstants.FILE_SIZE;
long outputSize = NetworkConstants.OUTPUT_SIZE;
int memory = 100;
UtilizationModel utilizationModel = new UtilizationModelFull();
int i = 0;
// Task A
NetworkCloudlet cl = new NetworkCloudlet(
NetworkConstants.currentCloudletId,
0,
1,
fileSize,
outputSize,
memory,
utilizationModel,
utilizationModel,
utilizationModel);
cl.numStage = 2;
NetworkConstants.currentCloudletId++;
cl.setUserId(userId);
cl.submittime = CloudSim.clock();
cl.currStagenum = -1;
cl.setVmId(vmIdList.get(i));
// first stage: big computation
cl.stages.add(new TaskStage(NetworkConstants.EXECUTION, 0, 1000 * 0.8, 0, memory, vmIdList.get(0), cl
.getCloudletId()));
cl.stages.add(new TaskStage(NetworkConstants.WAIT_SEND, 1000, 0, 1, memory, vmIdList.get(2), cl
.getCloudletId() + 2));
clist.add(cl);
i++;
// Task B
NetworkCloudlet clb = new NetworkCloudlet(
NetworkConstants.currentCloudletId,
0,
1,
fileSize,
outputSize,
memory,
utilizationModel,
utilizationModel,
utilizationModel);
clb.numStage = 2;
NetworkConstants.currentCloudletId++;
clb.setUserId(userId);
clb.submittime = CloudSim.clock();
clb.currStagenum = -1;
clb.setVmId(vmIdList.get(i));
// first stage: big computation
clb.stages.add(new TaskStage(
NetworkConstants.EXECUTION,
0,
1000 * 0.8,
0,
memory,
vmIdList.get(1),
clb.getCloudletId()));
clb.stages.add(new TaskStage(NetworkConstants.WAIT_SEND, 1000, 0, 1, memory, vmIdList.get(2), clb
.getCloudletId() + 1));
clist.add(clb);
i++;
// Task C
NetworkCloudlet clc = new NetworkCloudlet(
NetworkConstants.currentCloudletId,
0,
1,
fileSize,
outputSize,
memory,
utilizationModel,
utilizationModel,
utilizationModel);
clc.numStage = 2;
NetworkConstants.currentCloudletId++;
clc.setUserId(userId);
clc.submittime = CloudSim.clock();
clc.currStagenum = -1;
clc.setVmId(vmIdList.get(i));
// first stage: big computation
clc.stages.add(new TaskStage(NetworkConstants.WAIT_RECV, 1000, 0, 0, memory, vmIdList.get(0), cl
.getCloudletId()));
clc.stages.add(new TaskStage(NetworkConstants.WAIT_RECV, 1000, 0, 1, memory, vmIdList.get(1), cl
.getCloudletId() + 1));
clc.stages.add(new TaskStage(
NetworkConstants.EXECUTION,
0,
1000 * 0.8,
1,
memory,
vmIdList.get(0),
clc.getCloudletId()));
clist.add(clc);
}
这里便是创建工作流的详细过程,可见一个Appcloudlet内部是由多个NetworkCloudlet组成,这里创建的三个cloudlet执行关系如下:
这里初始化一个Networkcloudlet的步骤与创建一般的Cloudlet基本相同,不同的是Networkcloudlet加入了Stage(运行阶段)的概念,这里定义任务一有两个阶段,第一个阶段为执行,第二个阶段为WAIT_SEND
cl.stages.add(new TaskStage(NetworkConstants.EXECUTION, 0,1000 * 0.8, 0, memory, vmIdList.get(0), cl.getCloudletId()));
cl.stages.add(new TaskStage(NetworkConstants.WAIT_SEND, 1000, 0, 1, memory, vmIdList.get(2), cl.getCloudletId() + 2));
第一个参数是定义了这个阶段是做什么的,也就是阶段状态;
第二个参数是data;
第三个参数是执行时间,这里定义为1000*0.8,也就是800,所以无论VM和Host的mips有多大,这里定义就是一定执行够800的时钟长度;
第四个参数为stageid,代表这个任务的stage的id号;
第五个参数是这个任务运行所占内存;
第六个参数为任务运行所在的Vmid;
最后一个参数为目标任务,如果是执行,目标任务就是自己本身,如果是等待发送状态,目标任务就是这个任务即将发送给的对象;
4.运行示例
程序运行示例:
可以看出,任务2是在任务0和任务1之后进行执行的,任务0,1,2三个任务形成了一个工作流,这里我修改了broker创建Cloudlet的数量和VM的Mips,感兴趣的小伙伴可以自己在CloudSim下修改尝试。
总结
以上就是我关于NetworkDatacenter的学习体会,结合TestExample对如何建立自己的workflow和AppCloudlet进行了分析,从这个Example和部分Network包内的代码来看,感觉有点像是这个方面的工作完成度不高,同时程序内部自己写的注释来看也有一些问题,希望有能研究CloudSim时间比较长的大佬能够指点一二。
附上CloudSim学习的视频和源码:
嵌入式云计算 cloudsim
CloudSim/Github