Jul 4 2010

PeerSim 中文教程:Event-driven 模型

1. 介绍

本教程使用Event-driven模型来演示一个简单的例子,仍然使用的是gossip-based平均数协议,对消息的发送将进行更细节的建模;通过与cycle-based模型的对比,可以发现本协议存在的问题。

在Event-based模型中,除了时间管理和control传递给protocols的方式以外,其它与cycle-based模型相同。不可执行的Protocols(只用于存储数据,比如只存储邻居节点的linkable协议,或存储数值的vectors)可以以同样的方式应用和初始化,在peersim.cdsim包之外的controls也都可以使用。在默认情况下,在cycle-based模型中,controls会的每个周期中调用 ,但在event-based模型中,它们需要进行明确的调度,因为事件驱动模型并不存在周期的概念。

显然,我们可以编写专用于event-based模型的controls,即可以给协议发送事件(消息)。在很多情况下,这是必要的,因为系统经常完全或部分地由外部事件如用户的查询来驱动 ,这能很好地用由生成这些事件,并且驱动仿真执行的controls进行建模。

有些组件是不可用的。例如依赖于静态类peersim.cdsim.CDState(它提供了读取cycle相关的全局状态的接口)的所有组件。我们的经验是,很多依赖于这个状态的 cycle-based 组件可以经过简单的修改并删除这个依赖。

然而,可能有些令人吃惊的是,实现了cycle-based接口的peersim.cdsim.CDProtocol也可以使用于event-based模型,但是必须指出,在大部份的情况下这样做没有什么意义。然而,这个特性的有用之处在于:它让周期性地调用protocols变得很简单,这是一个几乎对所有与housekeeping,失效检测和sending heartbeat message有关的P2P协议来说典型的特性。

2. Protocol

下面以event-based模型来实现平均数协议:

package example.edaggregation;

import peersim.vector.SingleValueHolder;
import peersim.config.*;
import peersim.core.*;
import peersim.transport.Transport;
import peersim.cdsim.CDProtocol;
import peersim.edsim.EDProtocol;

/**
* Event driven version of epidemic averaging.
*/
public class AverageED extends SingleValueHolder
			implements CDProtocol, EDProtocol {

首先,这里同时实现了EDProtocol和CDProtocol接口,前者能让这个类能处理输入的消息,后者则可能令人困惑,因为它属于cycle-based模型的接口。但注意 event-based 的协议不是必须实现CDProtocol接口的,然而想要实现一个可以周期性取得control的协议时,可以通过实现CDProtocol接口,并在配置文件中设置一个CDScheduler 来实现。这样,代码就显得更清晰:以一个单独的组件来管理周期性的执行,而且能单独地进行配置。最后,我们还能简单地将 cycle-based 的协议移植到 event-based 模型上。

Continue reading


Jul 2 2010

PeerSim 中文教程:拓扑生成器

本教程描述了如何构建一个新的 PeerSim 拓扑生成器。

1. 什么是拓扑?为什么它很重要?

在一个大型的动态P2P系统中,节点没有关于整个网络的信息,而所有的节点都可能拥有一些邻居节点,即节点能”感知”的peers,这种”感知”的关系就定义了一个覆盖网络,这是P2P系统中的一个基本概念。

很多P2P协议都需要在多个不同的网络拓扑上进行实验。PeerSim中的peersim.dynamic.Wire*类已经包含了很多拓扑结构,可以直接用来对linkable协议进行初始化,本教程将展示如何构建一个自定义的拓扑生成器。

2. 一个用来模拟Internet的简单模型

下面我们将编写一个拓扑生成器来构建类似于Internet的树状拓扑,整个构建过程基于一个特定的,与位置相关的preferential attachment方法,编写规则很简单,并且会考虑几何和网络的限制以更好地模拟真实的网络。Preferential attachment由参数a来调整,这个参数能扩大或减少几何位置所带来的影响。

这个规则的策略如下:给定一个单位正方形,将x0置于中心,即x0 = (0.5,0.5),这个节点被称为root,令W()为与root相隔的跳数(hops),对于i=1 … n-1,随机在单位正方形中选择一个xi,然后选择使下面的表达式值最小的节点xj来连接它:

在这里dist()是欧几里德距离而a (alpha)是权重参数,显然,

通过这个方案我们得到了一个x0以为根的树。这个拓扑中每个节点(除了root外)的出度都为1,如果想更深入地理解这个模型,可以参考下面的文章:

  • Heuristically Optimized Trade-offs: A New Paradigm for Power Laws in the Internet
  • Degree distributions of the FKP network model
  • On Power-Law Relationships of the Internet Topology

3. 如何编码

我们的目标是编写一个可以根据 a (alpha)参数生成所需拓扑的PeerSim组件,并且能对生成的拓扑进行分析。这个拓扑可以在仿真过程中逐步生成,也可以用一个步骤生成拓扑,在这里我们倾向后者。为了构建需要的拓扑结构,我们需要下面的组件(注意这只是其中一种方案)。

  • 一个protocol 类,可以存储坐标,它不具备行为元素,只是一个普通的容器。
  • 一个initializer 类,可以为每个节点设置坐标值。
  • 一个control 类, 可在一个任意的linkable协议中根据坐标连接拓扑(在节点间添加link)
  • 一个observer 类,将拓扑结构打印到一个文件中(例如用GnuPlot对图进行可视化)。
  • 一个observer 类,用来收集节点入度的分布的统计数据
  • 一个observer 类,用来测试对随机节点失效的健壮性

在下节我们将看到,一些我们列出来的类是PeerSim中的基本组件,它们都实现了Linkable接口,Linkable以模块化的方式为用户提供了一个能处理任何拓扑结构的抽象。

Continue reading


Jul 2 2010

PeerSim 中文教程:编写一个新协议

本文的目的是在PeerSim中用Cycle-based模型实现一个简单的负载均衡算法。节点的状态有两种值:本地负载(local load)和配额(quota),其中配额是指节点在每个周期中允许传输的“负载”的大小。配额是必要的,是一个时间单元中能传输的负载上限。每个节点与和它距离最远的邻居节点交换配额值,这里“距离最远”是指与当前节点的负载差异最大。经过对比距离,协议将在负载均衡时选用push或pull的方式。

在每个周期之后,配额值将会被存储。协议并不关心拓扑管理,它依赖于其它组件来访问邻居节点(例如,Newscast,或者由IdleProtocol实现的静态拓扑)。

一. 必要的组件

一般来说只编写一个协议类是不足够的,还需要一些附加的组件。例如,为了在每个周期结束时为每个节点存储配额值,需要一个特定的Control对象。基本上来说,PeerSim是一个可替换的组件集合,所以在开发时需要注意模块化以让代码尽可能重用,出于这样的目的,我们这样设计下面的类:

  • protocol 它基于peersim.vector.SimpleValueHolder,这是一个简单的基类,用于访问一个浮点变量。Aggreation协议也使用了同样的基类。
  • ResetQuota 用于在每个周期结束时存储每个节点配额的control。
  • QuotaObserver 一个control,用于监视quota参数,即覆盖网中交换的流量大小。
  • initialization 这是在aggregation示例中的初始化器,这里也可以直接使用,因为它们实现了同样的接口SingleValueHolder。注意在example包中提供的初始化器是轻量级的,开发者应当更多地使用peersim.vector.*包中的初始化器。
  • observers 可以使用aggreagation.AverageObserver,因为这些组件实现了相同接口。

Continue reading


Jun 30 2010

PeerSim 中文教程:解析 Cycle-based 模式仿真

本文介绍了PeerSim的基本概念,并解析了两个示例以更清晰地说明PeeSim的仿真流程。

Peersim支持两种仿真模式,即Cycle-based的模型和传统的event-based的模型,本文专注于前者,

Cycle-based模型是一个简化的模型,拥有更好的伸缩性及性能,在拥有4GB内存的情况下,event-driven模式目前最多支持十万节点级别,而cycle-based模式则支持千万个节点级别。 但是Cycle-based模型缺少对传输层的仿真和并行处理,节点之间是直接通信的,仿真核心以一定的顺序周期性地给以节点控制。在运行时,可以进行任意的操作,如调用其它对象的方法并执行一些计算。

Cycle-based模型损失了一些真实性,虽然一些简单的协议可以忽略这些差别,但是在选择使用这个模型时,需要注意这些区别。我们可以相对简单地将Cycle-based的仿真移植到Event-driven引擎上,但在本文中不讨论这个话题。

一.基本介绍

PeerSim鼓励基于接口的模块化编程,每一个组件都能被其它实现了相同接口的组件代替,一般的仿真过程如下:

  1. 选择网络大小(即节点数量)。
  2. 选择要实验的一个或多个协议并进行初始化。
  3. 选择一个或多个Control对象来监视感兴趣的属性,并在仿真时修改一些参数(比如,网络大小,协议的内部状态,等等)。
  4. 根据配置文件,调用Simulator类运行仿真。

在仿真时创建的对象都是实现了一个或多个接口的类的实例,主要的接口如下所示:

Node P2P网络是由节点组成的,节点是协议的容器。Node接口提供了对节点所包含的协议的访问方法,并为节点提供了固定的ID。
CDProtocol 这是一个特定的协议,被设计用来在Cycle-based模型中运行,它只定义了在每一个周期中要运行的操作。
Linkable 一般都由协议来实现,这个接口为其它协议提供了访问邻居节点集合的服务,节点间相同的linkable协议类的实例定义了一个覆盖网络。
Control 实现了这个接口的类可以在仿真期间的某个时间点调度执行,这些类一般用于观察或修改仿真过程。

Cycle-based仿真的生命周期是这样的:

  1. 读取配置文件(通过命令行参数传递进来),然后仿真器初始化网络中的节点和节点中的协议,每个节点都拥有相同的协议栈。节点和协议的实例是通过克隆来创建的,只有一个原型是通过构造方法创建,其它的节点和协议都是从这个原型中克隆而来。基于这个原因,协议类中clone方法的实现是很重要的。
  2. 初始化操作,设置每个协议的初始状态。初始化阶段是由Control对象控制运行的,仅在实验开始时运行一次。在配置文件中,初始化的组件可以由init前缀识别,在下面讨论的initializer对象也是controls,但为了标记其功能以区别于一般的Control对象,它被配置用来在初始阶段运行。
  3. 在初始化完成后,Cycle-based引擎在每一个周期中调用所有组件(protocols和controls)一次,直到完成了指定的周期数,或者某个组件决定终止仿真为止。在PeerSim中每一个对象(controls和protocols)都被赋以一个Scheduler对象,它定义了什么时候本组件将会被执行。在默认情况下,所有对象都会在每个周期中运行。但我们也可以配置一个protocol或control只在某些特定的周期中运行,也可以在每一个周期中指定组件的执行顺序。

Continue reading


May 18 2010

用户访问行为,齐普夫分布及其 Java 实现

Zipf定律是文献计量学的重要定律之一,它和罗特卡定律、布拉德福定律一起被并称为文献计量学的三大定律。

对于CDN的内容管理,也近似符合Zipf 定律,就是大家常说对于内容的访问遵循80/20原则,也就是20%的内容,会占有80%的访问量。

zipf law

zipf law

这里 r 表示一个单词的出现频率的排名,P(r)表示排名为r的单词的出现频率.

(单词频率分布中 C约等于0.1, a约等于1)

后人将这个分布称为zipf distribution,中文名称为齐普夫分布或Zeta 分布。这是一个离散事件分布,广泛应用于语言学,保险学,网络模拟,以及对稀疏事件的建模中。

它表明在英语单词中,只有极少数的词被经常使用,而绝大多数词很少被使用。实际上,包括汉语在内的许多国家的语言都有这种特点。这个定律后来在很多领域得到了同样的验证,包括网站的访问者数量、城镇的大小和每个国家公司的数量。。这个定理也在很多分布里面得到了验证,比如人们的收入,互联网的网站数量和访问比例,互联网内容和访问比例(其他分布两个常数有所不同,a越大,分布越密集,对于VOD来说某些时候符合双zipf分布)。

比起枯燥的公式,图表更具有说服力,下面是用三百个严格符合zipf 分布的数据点描绘成的图,其中横轴表示排名,纵轴表示访问的频率,分别使用线性坐标和对数坐标表示:

zipf distribution

zipf distribution

可以看到对数坐标下是一条完美的直线。

Continue reading


Mar 6 2010

OMNeT++ 的仿真流程

Simulation with OMNeT++

用OMNeT++进行仿真的大致流程如下:

  1. 一个OMNeT++模型是用通过交换信息来通讯的组件(模块)来构建的。模块可以嵌套,也就是说,几个模块可以组成一个复合模块。在创建模型时,你需要将系统映射到一个相互通讯的模块体系中。
  2. 用NED语言定义模型的结构。你可以在OMNet++提供的IDE中以文本或图形化方式来编辑NED文件。
  3. 模型的活动组件(简单模块)需要用C++来编程,当中要使用仿真内核及类库。
  4. 提供一个拥有配置和参数的omnetpp.ini文件给模型,一个配置文件可以用不同的参数来描述若干个仿真过程。
  5. 构建仿真程序并运行它。你可以将代码链接到OMNet++的仿真内核及其提供的一个用户接口:命令行和交互式接口或图形化接口。
  6. 仿真结果将写入输出向量和输出标量文件中。你可以使用IDE中提供的分析工具来进行可视化。结果文件是普通的文本,所以你能用R,Matlab或其它工具来进行绘图。

原文在 >> 这里 <<。


Mar 5 2010

OMNet++ 10 分钟教程

OMNet++ in a nutshell

本文适合对网络模拟器有一定了解的读者,阅读本文时,最好同时打开用户手册和API文档以便随时查阅。

1. 在omnetpp.org中提到的仿真模型和框架与OMNet++是什么关系?

OMNet++提供了基本的工具和机制来编写仿真代码,但它本身并不提供任何特定用于计算机网络仿真,系统架构仿真和任意其它领域的组件;具体的仿真是由一些仿真模型和框架如Mobility Framework或INET Framework来支持,这些模型独立于OMNet++开发,并有自己的发布周期。

2. OMNet++提供了什么?

一个C++库,它由仿真内核及一些用来创建仿真组件(简单模块和信息)的工具类(如随机数生成,统计收集,拓扑发现等);组装和配置这些组件的基础设施(NED语言,ini文件);运行时用户接口或仿真环境(TKenv,Cmdenv);一个用来设计,运行和评估仿真的IDE环境;实时仿真的扩展接口;MRIP,并行的分布式仿真,数据库连接等等这些组成。

3. OMNet++的仿真模型是什么样的?

OMNet++提供了一个基于组件的架构,模型是由可重用的组件或模块组成的。模块之间可以通过gates(在其它系统中称为ports,即端口)进行连接,以构成复合模块。每个仿真模型是一个复合模块类型的实例。这一层次(组件和拓扑)由NED文件来处理。例如,一个名为EtherMAC的组件可以用NED来描述:

  //
  // Ethernet CSMA/CD MAC
  //
  simple EtherMAC {
      parameters:
          string address; // others omitted for brevity
      gates:
          input phyIn;    // to physical layer or the network
          output phyOut;  // to physical layer or the network
          input llcIn;    // to EtherLLC or higher layer
          output llcOut;  // to EtherLLC or higher layer
   }

它可以使用在下面的Ethernet station的模型中:

  //
  // Host with an Ethernet interface
  //
  module EtherStation {
      parameters: ...
      gates: ...
          input in;    // for connecting to switch/hub, etc
          output out;
      submodules:
          app: EtherTrafficGen;
          llc: EtherLLC;
          mac: EtherMAC;
      connections:
          app.out --> llc.hlIn;
          app.in < -- llc.hlOut;
          llc.macIn <-- mac.llcOut;           llc.macOout --> mac.llcIn;
          mac.phyIn < -- in;           mac.phyOut --> out;
  }

其中,注释能用来生成文档。简单模块,例如上面的EtherMAC,会与一个C++ 文件关联以提供行为,它是以simple关键字来声明的。复合模块则是用module关键字来声明的,为了仿真一个Ethernet LAN,应该创建一个复合模块EtherLAN并用network关键字来表示它可以通过自身运行。


network EtherLAN {
      ... (submodules of type EtherStation, etc) ...
  }

NED文件可以在IDE中以图形化方式或文件模式编辑。

NED文件只定义了模型的结构(拓扑),其行为和模块参数的某个子集则是开放的,如前面所提到的,行为是通过在简单模块相关联的C++代码来定义的,而在NED文件中没有赋值的模块参数则从ini文件中获取它们的值。

Continue reading


Jan 31 2010

OMNet++ 中的 NED 语言学习(4)

本节主要是关于信道(channel)的相关知识。

信道封装了与连接关联的参数和行为,信道与简单模块类似,在它们背后就是C++类,在默认情况下,类名与NED的类型名相同,除非有一个@class属性(@namespace也需要注意),例如,下面的信道类型需要一个名为CustomChannel 的C++ 类。

channel CustomChannel // needs a CustomChannel C++ class
{
}

跟简单模块不同的是,一般情况下不需要自己来定义信道类型,只需要从系统中已经预定义好的信道类型进行特殊化即可,内建的类型有: ned.IdealChannel, ned.DelayChannel and ned.Datarate-Channel。 (“ned” 是包名,和Java类似的是可以用import ned.* 来导入所有这三种类型,这样就不用写前缀ned)。

IdealChannel

  • 没有参数,可以让所有数据没有任何延迟地传送。

DelayChannel

  • 有两个参数。
  • delay是一个double类型的参数,代表传播时延,可以用s,ms,us来指定。
  • Disabled是一个布尔值,默认为false,当设为true时,信道对象会丢弃所有的信息。

DatarateChannel

  • 与DelayChannel相比,它有更多的参数。
  • datarate是一个double型的参数,代表信道的数据率,可以用bps,Kbps,Mbps,Gbps来指定,默认值是0,代表无限带宽。
  • ber和per分别代表比特错误率和包错误率,并且允许基本的错误建模。它们是随机生成的范围在0到1之间的数值。通过在包对象中设置一个错误的标记(error flag),接收的模块进行检查并丢弃标记为冲突的数据包。它们的默认值都是0。

注意: There is no channel parameter that would decide whether the channel deliversthe message object to the destination module at the end or at the start of the reception; that is decided by the C++ code of the target simple module. See the setDeliverOn-ReceptionStart() method of cGate.

Continue reading


Jan 28 2010

OMNet++ 中的 NED 语言学习(3)

本节内容是复合模块,即 Compound modules。

一个复合模块可以拥有gates和参数,但是并没有C++代码与之关联。如果想要为一个复合模块添加代码,可以将代码封装为一个简单模块,然后以子模块的方式来添加到复合模块中。

一个复合模块的声明可以包含若干部分,它们都是可选的:

module Host
{
         types: // 在这里定义内部类型,如模块和信道类型等,仅在本地使用
                   ...
         parameters:
                   ...
         gates:
                   ...
         submodules: // 子模块,可以创建子模块向量,子模块的类型可以来自参数
                   ...
         connections:// 可以通过循环,条件等创建连接,连接的行为可以通过将信道与连接联系来定义,信道的类型也可以来自参数
                   ...
}

复合模块可以通过继承来扩展,继承时不仅可以加入参数和gates,还可以加入新的子模块和新的连接,但是不能对子模块或连接进行“de-inherit”,或者修改所继承的模块或类型。

Continue reading


Jan 25 2010

OMNet++ 中的 NED 语言学习(2)

本节介绍简单模块。

简单模块是在模型中活动的组件,它用关键字 simple 来定义。

simple Queue
{
	parameters:
		int capacity;
		@display("i=block/queue");
	gates:
		input in;
		output out;
}

Parameters和gates部分都是可选的,即是说如果没有parameter或gate的话就可以不写。而且parameters关键字本身也是可选的,就算参数和属性存在也可以忽略它。

NED定义不包含模块的任何操作,操作是用C++来定义的,默认情况下OMNet将查找与NED类型同名的C++类(在这里是Queue)。

可以用@class 属性直接指定C++类,类也可以包括命名空间,如下例使用了mylib::Queue

simple Queue
{
	parameters:
		int capacity;
		@class(mylib::Queue);
		@display("i=block/queue");
	gates:
		input in;
		output out;
}

如果几个模块都在相同的命名空间里,那最好用@namespace代替@class属性,例如下面的例子会自动添加命名空间mylib作为前缀:

@namespace(mylib);
simple App {
	...
}
simple Router {
	...
}
simple Queue {
	...
}

Continue reading