PeerSim之我分析事件机制
进入main函数:
初始操作跟循环驱动差别不大,判断出事件机制后,进入nextExperiment()方法:
(1)初始操作
建立堆栈heap,其中主要存有时间数组times,事件数组events,节点数组nodes,协议号数组pids,Event类型(priorityQ.Event)的ev。另外从配置文件中读取相关参数,如endtime,logtime,size之类的。
(2)调用network.reset()设置网络
设置网络大小节点数组,根据原型节点(对于GeneralNode,其中包含节点protocol数组,index,ID等)来初始化网络中所有节点。节点中的Protocol[]保存协议书族实例。
(3)runInitializers()(运行时初始化,即调用私有静态成员函数)。
读取配置文件,根据init关键字获取初始化器的对象实例数组(构建出WireKOut对象,CDScheduler 对象和LinearDistribution对象)。获取初始化器对象后再按照顺序一个个调用这些初始化器的execute方法。
1 dynamics.WirekOut类:跟循环驱动一样。
2 edsim.CDScheduler类:
作用:仅初始化时调用一次,这里节点实现几个CDProtocl接口的协议,就相应新建几个NextCycleEvent事件。最后在该初始化器执行execute()方法时,每个节点将相应个数的NextCycleEvent事件加入堆中,改变的只是时间,其出堆执行的时间的设置是从Scheduler对象的step数值里随机产生一值。
注:入堆的是NextCycleEvent对象而非协议,而实现CDProtocol接口的协议的执行由NextCycleEvent对象控制。
在获取类实例时,将先执行CDScheduler类的static代码块,主要初始化Scheduler类型的sch数组(将CDProtocol类型的协议赋值)。CDScheduler此类构造函数实现:主要是存NextCycleEvent类型对象(该NCE对象的构造函数什么也不做)的数组,有几个实现CDProtocol的接口协议,就有几个Scheduler,因而也就创建有几个NextCycleEvent类型对象。
由配置文件:init.sch.protocol avg,其负责调度AvergeED。
CDScheduler执行execute()方法,只在初始化时调用一次。主要用来将实现CDProtocol接口的基于循环机制的协议可以在事件机制中运行:每个节点将自身所有实现的CDProtocol协议的pid以及相应的NextCycleEvent和下次调用的时间加入堆中。
注意:此堆是小堆,加入或者删除之后都要按照执行时间重新进行排序。
3 vector.LinearDistributionextends vectControl extends getter/setter:
根据配置文件init.vals.protocol avg,该初始化器主要初始化AvergeED协议(该协议实现了CDProtocol,EDProtocol接口,继承SingleValueholder类)。根据配置文件中的max、min计算出step值,据此在LinearDistribution类中调度execute()方法(通过反射机制调用SingleValueholder的setValue()方法)来设置此协议的初始值。
(4)scheduleControls()、
调度控制器。
作用:周期性调度执行Control类(通过execute()添加堆中)
该EDSimulator类中有两个成员:Control数组和Scheduler数组。获取Control类型的类实例,存储在Control数组中,相应也分配Scheduler类实例分配在Scheduler数组中。此配置文件中为:SingleValueObserver对象。最后将每一个对应的Control实例和Scheduler实例存放在ControlEvent对象(在peersim.edsim包中,为包访问权限,该对象有Control,Scheduler类型的成员和order,并且其构造数将其本身加入堆中)。Control时间的控制由step参数设定,该step参数最终将作为Scheduler调度器的step,用来控制下一次调度的时间。
(5)executeNext()
此函数首先取出小堆中的存储的时间最小的事件,将其保存PriorityQ.Event类型的变量ev中,根据此ev我们设置CommonState静态单例类。我们注意到在堆中存储的事件主要有:
(a) ControlEvent对象
此消息主要保存Control,Scheduler和order,主要用来周期性的调度该control的执行,调度时机有Scheduler控制。适用于周期性的动作,如周期性失效检测等。
首先执行此对象的Control类型的成员,在此配置文件下该对象是SingleValueObserver类型,其使用IncrementStates来统计网络中节点信息并输出。然后在重新设置order,其值为next+=step。并再次将此ControlEvent对象(修正order)存入堆中。
其实,个人认为可以将事件机制理解为循环机制的一种改进版。把一个step可以当做一个周期,首先执行的就是ControlEvent事件,执行完了再将它加入到下一周期的开始时。而节点产生的事件,其时间的产生本质而言就是在当前周期内时间的随机数(可理解为next+Random.next(step))。那么,在一个周期内所做的就是:首先执行ControlEvent事件,然后再执行所有节点上的NextCycleEvent事件,此执行顺序本质上是随机的。
(b) NextCycleEvent对象
此消息主要用来执行节点上实现了CDProtocol接口的协议,执行完毕再次加入堆中下次执行。因而其可以用来将循环机制转化到事件机制之中,其本质上也是周期性的执行。CDProtocol接口要求实现nextCycle()方法。
执行该对象的execute()方法,将根据CommonState类找到所要作用的协议、节点,然后调用该节点上的协议(这里是AvergeED协议)的nextCycle()方法。
该方法将利用UnreliableTransport传输层发送消息,其将根据配置文件中丢包情况调用UniformRandomTransport传输层的send()函数。发送消息本质上产生Avergemessage对象,并将之加入堆中,其时间的设置为CommonSatate.getTime()+delay(配置文件中配置)
发送完消息之后,再将此NextCycleEvent消息加入堆中,以便下一个周期中执行此消息,其中修改了其执行时间为CommonSatate.getTime()+step。
(c) AvergeMessage对象(为peersim.edaggregation包中类,包访问权限)
通过实现EDProtocol接口的协议(即执行processEvent())进行处理;
当节点收到此消息后,就会调用AvergeED协议的processEvent()方法。对于接受此消息的节点,它将做两件书:更新自身节点的值,即两节点值和的一半;同时发送一个应答消息给发送节点,此时应答消息发送节点成员设置为空。
然后,原始发送节点收到应答消息后,简单将自身节点值进行更新即可。
附在PeerSim中事件驱动流程简图: