ndnSIM-2.8 自定义缓存策略-LCD

Intro

本文旨在通过 ndnSIM 实现一个最简单的缓存策略,为后面自定义更加复杂的策略提供参考。注意ndnSIM 2.8版本的缓存实现与之前的版本有一些区别,因此不保证可以在其它版本下适用。

首先我们找到调用缓存插入是由谁调用的?

Forwarder::onIncomingData(),这个函数用于处理到达本节点的 data 包。在这里面会调用 m_cs.insert(data) 将到达本节点的 data 插入缓存。

1.  源码分析

下面我们来深入分析 Cs::insert()

// 如果设置了禁止 admit 任何数据,或者缓存容量为 0。直接 return,不缓存。

 if (!m_shouldAdmit || m_policy->getLimit() == 0) {
    return;
 }

// 取出 data 中携带的 CachePolicyTag,这个 tag 用于指示是否缓存。如果没有这个tag,则认为可以进行后续的缓存。如果这个tag指明了不缓存,直接return。

shared_ptr<lp::CachePolicyTag> tag = data.getTag<lp::CachePolicyTag>();
if (tag != nullptr) {
    lp::CachePolicyType policy = tag->get().getPolicy();
    if (policy == lp::CachePolicyType::NO_CACHE) {
        return false;
    }
}

// 将data压入m_table表,这个表用来存CS的内容。后面兴趣包转发时,逐跳查询是否命中缓存的时候,用的也是这个表。

const_iterator it;
bool isNewEntry = false;
std::tie(it, isNewEntry) = m_table.emplace(data.shared_from_this(), isUnsolicited);
Entry& entry = const_cast<Entry&>(*it);
entry.updateFreshUntil();

// 如果这个插入的 data 不是新的项,即缓存中已经存在了,则执行 refresh 操作,这对于不同的策略具有不同的操作(LRU & FIFO

if (!isNewEntry) { //
    if (entry.isUnsolicited() && !isUnsolicited) {
        entry.clearUnsolicited();
    }
    m_policy->afterRefresh(it); 
} 

// 如果是新的 data,就将它插入缓存队列中。 TODO:m_queue 和 m_table ?

else {
    m_policy->afterInsert(it); 
}

2.  缓存决策:LCD

Leave Copy Down策略,只在服务节点的下一跳节点进行缓存,即每次缓存都距离用户更近一跳。具体可参考:The LCD interconnection of LRU caches and its analysis

该技术最初是为分层 Web 缓存系统提出的。具体操作如下:每当内容请求从缓存或内容源获得服务时,内容只会在指向请求者的路径上的缓存层次结构中向下复制一层(即只在当前服务节点的下一跳缓存)。

这导致两个期望的效果:首先,内容在从服务节点到请求者的路径上只存储一次,从而减轻了冗余缓存的问题。 其次,内容逐渐向网络边缘复制,使得更流行的内容复制到更多节点,更靠近请求者。

该技术仅需要在节点间很少的协作,因为它们可以通过简单地向传输的内容中附加一个标志,来向下游的其他节点指示是否缓存。

3.  具体实现

3.1  自定义Tag的实现

用于指示节点是否缓存。源节点将其设为0,并添加到data包中。中间缓存节点接收到 data 包时,若发现这个 Tag 值为0,则缓存,并将该tag值置为1。否则直接转发。可参照原生的 HopCountTag 的实现,具体如下:

src/ndnSIM/ndn-cxx/ndn-cxx/lp/tags.hpp,添加以下:

/** \class LCDTag
 *  \brief a packet tag for LCD
 *
 * This tag can be attached to Interest, Data, Nack.
 */
typedef SimpleTag<uint64_t, 0x60000002> LCDTag;

src/ndnSIM/ndn-cxx/ndn-cxx/lp/tlv.hpp,添加以下:

/**
 * \brief TLV-TYPE numbers for NDNLPv2
 */
enum {
    ...
    LCDTag = 90,
}

src/ndnSIM/ndn-cxx/ndn-cxx/lp/fields.hpp,添加以下

typedef FieldDecl<field_location_tags::Header, uint16_t, tlv::LCDTag, false, NonNegativeIntegerTag,
                  NonNegativeIntegerTag>
  LCDTagField;
BOOST_CONCEPT_ASSERT((Field<LCDTagField>));

...

/** \brief Set of all field declarations.
 */
typedef boost::mpl::set<FragmentField, SequenceField, FragIndexField, FragCountField, PitTokenField, NackField,
                        NextHopFaceIdField, IncomingFaceIdField, CachePolicyField, CongestionMarkField, AckField,
                        TxSequenceField, NonDiscoveryField, PrefixAnnouncementField, HopCountTagField, LCDTagField, GeoTagField>
  FieldSet;

3.2  自定义Tag的处理

src/ndnSIM/NFD/daemon/face/generic-link-service.cpp,做以下修改:


(1) 节点收到 data 后如何处理它的 tag ?不做更改,交给后面的缓存策略来决定。

void
GenericLinkService::decodeData(const Block& netPkt, const lp::Packet& firstPkt, const EndpointId& endpointId)
{
    ...

    if (firstPkt.has<lp::LCDTagField>()) { // 收到的包, 如果有LCDTag, 不在这里修改
		data->setTag(make_shared<lp::LCDTag>(firstPkt.get<lp::LCDTagField>())); 
    }

}

 (2) 本节点转发和缓存操作完成后,这里是给发出去的 data 包,打上这个 tag。(注意前面的缓存操作有可能会修改这个 tag 值

void
GenericLinkService::encodeLpFields(const ndn::PacketBase& netPkt, lp::Packet& lpPacket) { 
    ...

    shared_ptr<lp::LCDTag> lcdTag = netPkt.getTag<lp::LCDTag>();
    if (lcdTag != nullptr) { // 如果有这个tag, 直接去拿它的值
		lpPacket.add<lp::LCDTagField>(*lcdTag);
    }
    else { // 如果没有 我们去添加这个tag 并将它的字段值设置为0 
        // 什么时候没有? Producer节点, 或缓存命中节点.
        lpPacket.add<lp::LCDTagField>(0); 
    }
}

3.3  缓存策略的实现

src/ndnSIM/NFD/daemon/fw/forwarder.cpp中,做以下修改:根据缓存决策的结果,对 data 中的  LCDTag 值做对应的修改。

void
Forwarder::onIncomingData(const FaceEndpoint& ingress, const Data& data) 
{
    ...
    // 把原来这个注掉, 它相当于缓存每一个内容, 即 LCE
    // m_cs.insert(data);
    
    // Added: 
    // 缓存决策: 你可以直接在这里实现, 也可以在 Cs::insert() 里面实现
    bool cacheDecision = m_cs.insert(data);
	if (cacheDecision) { 
		data.setTag(make_shared<lp::LCDTag>(1)); 
	}
    ...	
}

src/ndnSIM/NFD/daemon/table/cs.cpp中,做以下修改:(1)返回值类型改为bool,这是为了返回缓存决策结果。(2)添加 LCD 缓存决策函数的实现。

// modified: 
bool
Cs::insert(const Data& data, bool isUnsolicited)
{
    if (!m_shouldAdmit || m_policy->getLimit() == 0) {
        return false;
    }
    shared_ptr<lp::CachePolicyTag> tag = data.getTag<lp::CachePolicyTag>();
    if (tag != nullptr) {
        lp::CachePolicyType policy = tag->get().getPolicy();
        if (policy == lp::CachePolicyType::NO_CACHE) {
            return false; 
        }
    }

	// added: cache decision
	bool cacheDecRes = cacheDecisionLCD(data);
	if (!cacheDecRes) 
		return false;
	NFD_LOG_DEBUG("insert " << data.getName());

	const_iterator it;
    bool isNewEntry = false;
    std::tie(it, isNewEntry) = m_table.emplace(data.shared_from_this(), isUnsolicited); 
    Entry& entry = const_cast<Entry&>(*it);
    entry.updateFreshUntil(); 

    if (!isNewEntry) {
        if (entry.isUnsolicited() && !isUnsolicited) {
            entry.clearUnsolicited();
        }
        m_policy->afterRefresh(it); 
    } 
	else { 
		m_policy->afterInsert(it); 
	}
	return true;
}
// added:
bool
Cs::cacheDecisionLCD(const Data& data) 
{
	// (1) if management protocol --> not insert
	bool isData = true;
	std::string testStr = "/localhost/nfd/";
  	std::string dataName = data.getName().toUri();
  	std::string::size_type idx = dataName.find(testStr);
  	if (idx != std::string::npos) { // match ---> 即不是常规data项
		isData = false;
		return false;
	}

	// (2) content  
	auto lcdTag = data.getTag<lp::LCDTag>();  
	int lcd_tag = 0;
	if (lcdTag != nullptr) { 
		lcd_tag = *lcdTag;
	} 
	if (lcd_tag == 0) { // 有2种情形: (1) Producer响应数据,tag为null; (2) 缓存节点响应,*tag为0
		return true;
	} else {
		return false; 
	}
}

src/ndnSIM/NFD/daemon/table/cs.hpp中,做以下修改:即上面的函数声明。

...

public:
  bool insert(const Data& data, bool isUnsolicited = false);
  bool cacheDecisionLCD(const Data& data);

...

4.  测试

仍然使用 src/ndnSIM/examples/ndn-tree-cs-tracers.cpp 进行测试,我们对打印做了一些过滤和修改,输出如下:

'build' finished successfully (38.182s)
+0.000000000s 0 ndn.Consumer:Consumer()
+0.000000000s 1 ndn.Consumer:Consumer()
+0.000000000s 0 ndn.Consumer:StartApplication()
+0.000000000s 2 ndn.Consumer:Consumer()
+0.000000000s 0 ndn.Consumer:SendPacket(): [INFO ] > Interest for 0
+0.000000000s 0 ndn.Consumer:WillSendOutInterest(): [DEBUG] Trying to add 0 with +0.0ns. already 0 items
+0.000000000s 0 ndn-cxx.nfd.ContentStore:findImpl(): [DEBUG] find /root/%FE%00 no-match
+0.000000000s 3 ndn.Consumer:Consumer()
+0.000000000s 0 ndn.Consumer:OnNack(): [INFO ] NACK received for: /root/%FE%00, reason: NoRoute
+0.000000000s 6 ndn.Producer:Producer()
+0.000000000s 6 ndn.Producer:StartApplication()
+0.010000000s 1 ndn.Consumer:StartApplication()
+0.010000000s 1 ndn.Consumer:SendPacket(): [INFO ] > Interest for 0
+0.010000000s 1 ndn.Consumer:WillSendOutInterest(): [DEBUG] Trying to add 0 with +10000000.0ns. already 0 items
+0.010000000s 1 ndn-cxx.nfd.ContentStore:findImpl(): [DEBUG] find /root/%FE%00 no-match
+0.011028799s 4 ndn-cxx.nfd.ContentStore:findImpl(): [DEBUG] find /root/%FE%00 no-match
+0.012057598s 6 ndn-cxx.nfd.ContentStore:findImpl(): [DEBUG] find /root/%FE%00 no-match
+0.012057598s 6 ndn.Producer:OnInterest(0x5648ffd74c00, /root/%FE%00?Nonce=4231791817&Lifetime=2000)
+0.012057598s 6 ndn.Producer:OnInterest(): [INFO ] node(6) responding with Data: /root/%FE%00

# 仅在节点 6 缓存
+0.012057598s 6 ndn-cxx.nfd.ContentStore:insert(): [DEBUG] insert /root/%FE%00
+0.015769596s 1 ndn.Consumer:OnData(0x5648ffe19440, Name: /root/%FE%00
MetaInfo: ContentType: 0
Content: (size: 1024)
Signature: (type: Unknown(255), value_length: 1)
)
+0.015769596s 1 ndn.Consumer:OnData(): [DEBUG] Hop count: 2
+0.015769596s 1 ndn.Consumer:OnData(): [DEBUG] LCD TAG: 1
+0.020000000s 2 ndn.Consumer:StartApplication()
+0.020000000s 2 ndn.Consumer:SendPacket(): [INFO ] > Interest for 0
+0.020000000s 2 ndn.Consumer:WillSendOutInterest(): [DEBUG] Trying to add 0 with +20000000.0ns. already 0 items
+0.020000000s 2 ndn-cxx.nfd.ContentStore:findImpl(): [DEBUG] find /root/%FE%00 no-match
+0.021028799s 5 ndn-cxx.nfd.ContentStore:findImpl(): [DEBUG] find /root/%FE%00 no-match
+0.022057598s 6 ndn-cxx.nfd.ContentStore:findImpl(): [DEBUG] find /root/%FE%00 matching /root/%FE%00

# 仅在节点5缓存
+0.023913597s 5 ndn-cxx.nfd.ContentStore:insert(): [DEBUG] insert /root/%FE%00
+0.025769596s 2 ndn.Consumer:OnData(0x5648ffedbf50, Name: /root/%FE%00
MetaInfo: ContentType: 0
Content: (size: 1024)
Signature: (type: Unknown(255), value_length: 1)
)
+0.025769596s 2 ndn.Consumer:OnData(): [DEBUG] Hop count: 2
+0.025769596s 2 ndn.Consumer:OnData(): [DEBUG] LCD TAG: 1
+0.029999999s 3 ndn.Consumer:StartApplication()
+0.029999999s 3 ndn.Consumer:SendPacket(): [INFO ] > Interest for 0
+0.029999999s 3 ndn.Consumer:WillSendOutInterest(): [DEBUG] Trying to add 0 with +29999999.0ns. already 0 items
+0.029999999s 3 ndn-cxx.nfd.ContentStore:findImpl(): [DEBUG] find /root/%FE%00 no-match
+0.031028798s 5 ndn-cxx.nfd.ContentStore:findImpl(): [DEBUG] find /root/%FE%00 matching /root/%FE%00

# 仅在节点3缓存
+0.032884797s 3 ndn-cxx.nfd.ContentStore:insert(): [DEBUG] insert /root/%FE%00
+0.032884797s 3 ndn.Consumer:OnData(0x5648ffdf2920, Name: /root/%FE%00
MetaInfo: ContentType: 0
Content: (size: 1024)
Signature: (type: Unknown(255), value_length: 1)
)
+0.032884797s 3 ndn.Consumer:OnData(): [DEBUG] Hop count: 1
+0.032884797s 3 ndn.Consumer:OnData(): [DEBUG] LCD TAG: 1

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值