3月28日学习总结

本文介绍了分布式网络中三种不同的路由策略:Epidemic路由采用泛洪机制,SprayAndWait路由限制消息复制次数,而Prophet算法利用历史相遇信息动态更新节点间的投递预测值。Epidemic通过邻居节点交换消息,SprayAndWait则按一定规则分配消息副本,Prophet则基于节点相遇频率和传递性优化消息传递。
摘要由CSDN通过智能技术生成

一、ONE平台路由模块学习

1、Epidemic路由

DirectDelivery路由是一个极端,即从不复制消息,只有碰到目的节点,才交付消息。Epidemic是另一个极端,采用泛洪(Flooding)机制,只要有机会,就将消息传递给邻居节点。

当节点相遇时(如A遇见B),A将其summary vector传递给B,B可以求得两节点消息队列的差集,发给A,如此,A便可知道传递哪些消息给B,即A有但B没有的消息。 示意图如下:

 

1.1 Epidemic路由算法的Update()源码如下:

@Override
	public void update() {
		super.update();
		if (isTransferring() || !canStartTransfer()) {
			return; // transferring, don't try other connections yet
		}
		
		// Try first the messages that can be delivered to final recipient
		//请先尝试可以传递给目的节点的消息
		if (exchangeDeliverableMessages() != null) {
			return; // started a transfer, don't try others (yet)
		}
		
		// then try any/all message to any/all connection
//		尝试将此路由器承载的所有消息发送到此节点的所有邻居节点
		this.tryAllMessagesToAllConnections();
	}

EpidemicRouterDirectDiliveryRouter基础上添加this.tryAllMessagesToAllConnections()

 1.2 tryAllMessagesToAllConnections

tryAllMessagesToAllConnections遍历所有connections(相当于遍历邻居节点),遍历节点A缓冲区的消息,若有消息能传输,则返回。相关源代码及注释如下:

    //ActiveRouter.java
    protected Connection tryAllMessagesToAllConnections() {
        List<Connection> connections = getConnections();  //取得所有邻居节点
        if (connections.size() == 0 || this.getNrofMessages() == 0) {
            return null;
        }
     
        List<Message> messages = new ArrayList<Message>(this.getMessageCollection()); //取得缓冲区所有消息
        this.sortByQueueMode(messages);
     
        return tryMessagesToConnections(messages, connections);
    }
     
    //tryMessagesToConnections(messages, connections);
    protected Connection tryMessagesToConnections(List<Message> messages, List<Connection> connections) {
        for (int i=0, n=connections.size(); i<n; i++) {
            Connection con = connections.get(i);
            Message started = tryAllMessages(con, messages);
            if (started != null) {
                return con;
            }
        }
        return null;
    }
     
    //tryAllMessages(con, messages); 只要有一个消息能传递,就返回
    protected Message tryAllMessages(Connection con, List<Message> messages) {
        for (Message m : messages) {
            int retVal = startTransfer(m, con);
            if (retVal == RCV_OK) {  //accepted a message, don't try others
                return m;
            } else if (retVal > 0) { //系统定义,只有TRY_LATER_BUSY大于0,即为1
                return null;          // should try later -> don't bother trying others
            }
        }
        return null; // no message was accepted
    }

2、SprayAndWait路由

相对于Epidemic,SprayAndWait降低消息在网络中的份数,即对复制次数做一些限制。正如其名,SprayAndWait包含两个阶段(假设每个消息初始化为L copies,即网络中含有某消息最多L份):

  • Spray:当碰到一个节点时,决定多少份给该节点。最简单的分发策略是一份一份地发;还有另一种分发策略是每次将一半copies给遇到的节点(Binary Spray and Wait),当节点只有一份消息时,就退化成DirectDelivery了
  • Wait:只要copies大于1,就将消息分给碰到的节点,直到1。当节点只有一份的时候,就等待碰到目的节点(想想DirectDelivery)

减半分发(Binary Spray and Wait)是最优的(the minimum expected delay)。确切地说,节点An copies,遇到节点B,此时将n/2 copies(上取整)给B,节点A留下n/2 copies(下取整)。

2.1 设置文件

SprayAndWait需要事先知道消息的份数以及分发策略,相关设置字段如下:

//_settings.txt
Group.router = SprayAndWaitRouter
SprayAndWaitRouter.nrofCopies = 10      //消息的份数
SprayAndWaitRouter.binaryMode = true    //true为一半一半地分,false为一份一份地分
 
//SprayAndWaitRouter.java
//identifier for the initial number of copies setting
public static final String NROF_COPIES = "nrofCopies"; 
//identifier for the binary-mode setting
public static final String BINARY_MODE = "binaryMode"; 
//SprayAndWait router's settings name space
public static final String SPRAYANDWAIT_NS = "SprayAndWaitRouter";  
 
public SprayAndWaitRouter(Settings s) {
    super(s);
    Settings snwSettings = new Settings(SPRAYANDWAIT_NS);
    initialNrofCopies = snwSettings.getInt(NROF_COPIES);
    isBinary = snwSettings.getBoolean( BINARY_MODE);
}

2.2 增加消息字段

每个消息需要一个字段保存份数copies,因此,需要为Message添加新的字段。之前的做法是直接修改Message类,今天看了SprayAndWaitRouter.java的源码,可以重写createNewMessage,在原来基础上利用函数msg.addProperty添加字段。源代码如下:

/**
	 * 每个消息需要一个字段保存份数copies,
	 * 因此,需要为Message添加新的字段。我之前的做法是直接修改Message类,
	 * 今天看了SprayAndWaitRouter.java的源码,
	 * 原来还可以这样:重写createNewMessage,在原来基础上利用函数msg.addProperty添加字段。
	 */
	@Override 
	public boolean createNewMessage(Message msg) {
		makeRoomForNewMessage(msg.getSize());
		msg.setTtl(this.msgTtl);
		/**
		 * addProperty(MSG_COUNT_PROPERTY, new Integer(initialNrofCopies))
		 * 为此消息添加一个通用属性。键可以是任何字符串,
		 * 但它应该是这样的,以便其他类不会意外使用相同的值。
		 * 该值可以是任何对象,但最好只存储不可变的对象,
		 * 因为当复制消息时,只会生成属性的浅副本。
		 */
		msg.addProperty(MSG_COUNT_PROPERTY, new Integer(initialNrofCopies));
		addToMessages(msg, true);
		return true;
	}

取得和更新该字段值的方法如下:

Integer nrofCopies = (Integer)m.getProperty(MSG_COUNT_PROPERTY); //取值
msg.updateProperty(MSG_COUNT_PROPERTY, nrofCopies);              //更新

2.3 update

SprayAndWait的update函数很简单,类似于Epidemic的tryAllMessagesToAllConnections,但更严格,即消息队列过滤掉那些copies1的消息。源代码如下:

@Override
	public void update() {
		super.update();
		if (!canStartTransfer() || isTransferring()) {
			return; // nothing to transfer or is currently transferring 
		}

		/* try messages that could be delivered to final recipient */
		if (exchangeDeliverableMessages() != null) {
			return;
		}
		//获取缓冲区有效消息集合,即copies大于1的消息
		/* create a list of SAWMessages that have copies left to distribute */
		@SuppressWarnings(value = "unchecked")
		List<Message> copiesLeft = sortByQueueMode(getMessagesWithCopiesLeft());
		
		if (copiesLeft.size() > 0) {
			/* try to send those messages */
//			this.tryMessagesToConnections(copiesLeft, getConnections());
			this.tryMessagesToConnections(copiesLeft, getConnections());
		}
	}

getMessagesWithCopiesLeft()函数在getMessageCollection()的基础上,过滤掉那些copies小于1的消息,源代码如下:

/**
	 * Creates and returns a list of messages this router is currently
	 * carrying and still has copies left to distribute (nrof copies > 1).
	 * 创建并返回此路由器当前承载的消息列表,并且仍有副本要分发
	 * getMessagesWithCopiesLeft在getMessageCollection()的基础上,过滤掉那些copies小于1的消息
	 * @return A list of messages that have copies left
	 */
	protected List<Message> getMessagesWithCopiesLeft() {
		List<Message> list = new ArrayList<Message>();

		for (Message m : getMessageCollection()) {
			Integer nrofCopies = (Integer)m.getProperty(MSG_COUNT_PROPERTY);
			assert nrofCopies != null : "SnW message " + m + " didn't have " + 
				"nrof copies property!";
			if (nrofCopies > 1) {
				list.add(m);
			}
		}
		//返回的是消息副本数大于1的消息列表
		return list;
	}

2.4 更新消息的份数

假设A发送消息mB,那么传输完毕后,AB的缓冲区都有m,都需要对m更新copies。值得注意的是,更新发送端和接收端都由A发起,源代码如下:

    //ActiveRouter.java
    public void update() {
        super.update();
        ...
        if (con.isMessageTransferred()) {  //消息是否传输完毕,即是否过了(1.0*m.getSize())/this.speed没
            if (con.getMessage() != null) {
                transferDone(con);       //更新发送端
               con.finalizeTransfer();    //更新接收端
            }
        }
        ...
    }
     
    //Connection.java    更新接收端
    public void finalizeTransfer() {
        this.bytesTransferred += msgOnFly.getSize();
        getOtherNode(msgFromNode).messageTransferred(this.msgOnFly.getId(), msgFromNode); //更新接收端
        clearMsgOnFly();
    }

2.4.1 更新发送端

/**
	 * 在传输完成前调用
	 * 重写了父类的transferDone 用来更新发送端
	 * Called just before a transfer is finalized (by 
	 * {@link ActiveRouter#update()}).
	 * Reduces the number of copies we have left for a message. 
	 * In binary Spray and Wait, sending host is left with floor(n/2) copies,
	 * but in standard mode, nrof copies left is reduced by one. 
	 */
	@Override
	protected void transferDone(Connection con) {	//更新发送端
		//步骤1:取得消息的copies
		Integer nrofCopies;
		String msgId = con.getMessage().getId();
		/* get this router's copy of the message */
		Message msg = getMessage(msgId);

		if (msg == null) { // message has been dropped from the buffer after..
			return; // ..start of transfer -> no need to reduce amount of copies
		}
		
		/* reduce the amount of copies left */
		nrofCopies = (Integer)msg.getProperty(MSG_COUNT_PROPERTY);
		 //步骤2:更新消息的copies
		if (isBinary) { 
			nrofCopies /= 2;
		}
		else {
			nrofCopies--;
		}
		//更新
		msg.updateProperty(MSG_COUNT_PROPERTY, nrofCopies);
	}

2.4.2 更新接收端

	//更新接收端 	重写了父类的messageTransferred用来更新接收端
	@Override
	public Message messageTransferred(String id, DTNHost from) {	
		Message msg = super.messageTransferred(id, from);
		Integer nrofCopies = (Integer)msg.getProperty(MSG_COUNT_PROPERTY);	//获取消息的副本数
		
		assert nrofCopies != null : "Not a SnW message: " + msg;
		
		if (isBinary) {
			/* in binary S'n'W the receiving node gets ceil(n/2) copies */
			nrofCopies = (int)Math.ceil(nrofCopies/2.0);
		}
		else {
			/* in standard S'n'W the receiving node gets only single copy */
			nrofCopies = 1;
		}
		
		msg.updateProperty(MSG_COUNT_PROPERTY, nrofCopies);	//更新
		return msg;
	}

3、Prophet算法

Prophet利用节点间之前的相遇情况为每个节点对求得投递预测值delivery predictabilities,若转发节点的delivery predictabilities高于发送节点,那么就将消息复制转发。

Prophet就是用节点间之前相遇的历史信息,动态更新链接的投递可能值(delivery predictabilities)。求delivery predictabilities包含3部分,公式如下:

(1)encounter

节点间相遇越频繁说明delivery predictability越高,Pinit是一个初始化常数,在[0,1]间取值。

(2)age

如果两个节点间有一段时间没相遇了,那么delivery predictability需要降低。Gamma是指aging constant,在[0,1)间取值,k指距离上次更新的time units数。

(3)transitive

考虑节点间的传递性,如节点A经常遇到节点BB经常遇见C,那么连接(a,c)delivery predictability也应该更高。β是放大常数,在[0,1]间取值。

 3.1 常数及设置文件

由上述分析可知,Prophet涉及到三个常数和一个变量:Pinit, Gamma, β, k。三个常数如下:

    //ProphetRouter.java
    public static final double P_INIT = 0.75;       //delivery predictability initialization constant
    public static final double DEFAULT_BETA = 0.25; //delivery predictability transitivity scaling constant default value
    public static final double GAMMA = 0.98;        //delivery predictability aging constant
     
    private double beta;    //可以通过设置文件设置,如ProphetRouter.beta = 0.25

3.2 更新delivery predictabilities

首先需要存储每个节点对的delivery predictabilities,用HashMap存储。每个节点的私有变量preds存储本节点与其他节点的delivery predictabilities,如(a, b), (a, c), (a, …)。源代码如下:

    //ProphetRouter.java
    private Map<DTNHost, Double> preds; //delivery predictabilities
     
    private void initPreds() {
        this.preds = new HashMap<DTNHost, Double>();
    }

3.2.1 何时更新

对于encountertransitive,当链接建立时更新;对于age,每隔secondsInTimeUnit更新一次。值得注意的是,更新age比较被动。updateDeliveryPredForupdateTransitivePreds都会调用getPredFor,取得节点的delivery predictabilitiesgetPredFor函数会调用ageDeliveryPreds更新因age带来的影响。源代码如下:

    //ProphetRouter.java    
    @Override
    public void changedConnection(Connection con) {
        super.changedConnection(con);
     
        if (con.isUp()) {
            DTNHost otherHost = con.getOtherNode(getHost());
            updateDeliveryPredFor(otherHost);
            updateTransitivePreds(otherHost);
        }
    }
     
    public double getPredFor(DTNHost host) {
        ageDeliveryPreds();         //make sure preds are updated before getting
        if (preds.containsKey(host)) {
            return preds.get(host);
        } else {
            return 0;
        }
    }

3.2.2 因encounter更新

假设节点A与节点B相遇(即连接建立),轮到节点Aupdate,更新的是节点A上的preds(a, b)delivery predictabilities。源代码如下:

    //ProphetRouter.java
    private void updateDeliveryPredFor(DTNHost host) {
        double oldValue = getPredFor(host);  //取值之前需要更新age
        double newValue = oldValue + (1 - oldValue) * P_INIT;
        preds.put(host, newValue);
    }
     
    public double getPredFor(DTNHost host) {
        ageDeliveryPreds();         //make sure preds are updated before getting
        if (preds.containsKey(host)) {
            return preds.get(host);
        } else {
            return 0;
        }
    }

3.2.3 因age更新

每隔secondsInTimeUnit,降低所有节点的delivery predictabilities,源代码如下:

/**
	 * Ages all entries in the delivery predictions.
	 * 对投递预测中的所有条目进行老化
	 * <CODE>P(a,b) = P(a,b)_old * (GAMMA ^ k)</CODE>, where k is number of
	 * time units that have elapsed since the last time the metric was aged.
	 * 其中k是自度量上次老化以来经过的时间单位数。
	 * @see #SECONDS_IN_UNIT_S
	 */
	private void ageDeliveryPreds() {
		double timeDiff = (SimClock.getTime() - this.lastAgeUpdate) / 
			secondsInTimeUnit;
		
		if (timeDiff == 0) {	//如果没超过secondsInTimeUnit,就不更新
			return;
		}
		//所有的节点都乘以GAMMA ^ k
		double mult = Math.pow(GAMMA, timeDiff);
		for (Map.Entry<DTNHost, Double> e : preds.entrySet()) {
			e.setValue(e.getValue()*mult);
		}
		
		this.lastAgeUpdate = SimClock.getTime();
	}

3.2.4 因transitive更新

/**
	 * Updates transitive (A->B->C) delivery predictions.
	 * <CODE>P(a,c) = P(a,c)_old + (1 - P(a,c)_old) * P(a,b) * P(b,c) * BETA
	 * </CODE>
	 * @param host The B host who we just met
	 */
	//updateTransitivePreds(otherHost); 假设a--b,节点a做update,那么下述的host是指b
	private void updateTransitivePreds(DTNHost host) {
		MessageRouter otherRouter = host.getRouter();
		assert otherRouter instanceof ProphetRouter : "PRoPHET only works " + 
			" with other routers of same type";
		
		double pForHost = getPredFor(host); // P(a,b)
		Map<DTNHost, Double> othersPreds = 
			((ProphetRouter)otherRouter).getDeliveryPreds();
		
		for (Map.Entry<DTNHost, Double> e : othersPreds.entrySet()) {
			if (e.getKey() == getHost()) {	//此时getHost()是指节点a
				continue; // don't add yourself
			}
			
			double pOld = getPredFor(e.getKey()); //P(a,c)_old, 实为节点a的getPredFor,相当于this.getPredFor
			double pNew = pOld + ( 1 - pOld) * pForHost * e.getValue() * beta;	//e.getValue为P(b, c)
			preds.put(e.getKey(), pNew);
		}
	}

3.3 update

ProphetRouter的update在DirectDelivery基础上,调用tryOtherMessages函数,Prophet只将消息发送给delivery predictabilities更高的节点。举例说明,节点A遇到节点B,节点A是否要将消息m(其目的节点为C)复制转发给节点B,答案是若P(b, c)大于P(a, c),那么就将消息m复制转发给B。源代码如下:

@Override
	public void update() {
		super.update();
		if (!canStartTransfer() ||isTransferring()) {
			return; // nothing to transfer or is currently transferring 
		}
		
		// try messages that could be delivered to final recipient
		if (exchangeDeliverableMessages() != null) {
			return;
		}
		
		tryOtherMessages();		
	}
	
	/**
	 * Tries to send all other messages to all connected hosts ordered by
	 * their delivery probability	尝试将所有其他消息发送到所有连接的主机,按其传递概率排序
	 * @return The return value of {@link #tryMessagesForConnected(List)}
	 */
	private Tuple<Message, Connection> tryOtherMessages() {
		List<Tuple<Message, Connection>> messages = 
			new ArrayList<Tuple<Message, Connection>>(); 
	
		Collection<Message> msgCollection = getMessageCollection();
		
		/* for all connected hosts collect all messages that have a higher
		   probability of delivery by the other host 对于所有连接的主机,收集其他主机传递概率较高的所有消息 */
		for (Connection con : getConnections()) {
			DTNHost other = con.getOtherNode(getHost());
			ProphetRouter othRouter = (ProphetRouter)other.getRouter();
			
			if (othRouter.isTransferring()) {
				continue; // skip hosts that are transferring
			}
			
			for (Message m : msgCollection) {
				if (othRouter.hasMessage(m.getId())) {
					continue; // skip messages that the other one has
				}
				//核心代码
				if (othRouter.getPredFor(m.getTo()) > getPredFor(m.getTo())) {	//邻居节点到目的节点的概率 大于 本节点到邻居节点的概率 
					// the other node has higher probability of delivery
					messages.add(new Tuple<Message, Connection>(m,con));
				}
			}			
		}
		
		if (messages.size() == 0) {
			return null;
		}
		
		// sort the message-connection tuples
		//通过连接另一侧的主机按元组的传递概率对元组进行排序
		Collections.sort(messages, new TupleComparator());
		return tryMessagesForConnected(messages);	// try to send messages
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值