欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Fabric中的Second-Chance缓存淘汰算法的实现分析

程序员文章站 2022-05-13 19:59:09
...

一、概述

1.1 Fabric MSP

Fabric引入MSP(Membership Service Provider),即成员关系服务提供者,这一抽象化的模块组件来管理Fabric中的众多参与者(peer、orderer等)。
MSP将颁发证书和校验证书,以及用户认证背后的所有密码学机制与协议都抽象了出来。对Fabric网络中的成员进行身份的管理(身份验证)与认证(签名与验签)。

成员进行身份验证时,提供的是X.509的证书,所以MSP会频繁的反序列化验证这些X.509证书,此时MSP模块就利用Second-Chance算法来缓存一些解析验证的结果,从而提高性能。

1.2 Second-Chance Algorithm

Second-Chance算法利用FIFO,在在淘汰对象时,会检查待淘汰对象的引用标志位,如果对象被引用过,该对象引用位清零,重新插入队列尾部,像新的对象一样;如果该对象未被引用过,则将被淘汰。

原理如下:

在FIFO算法的基础上,

  • 为缓存中的所有对象增加一个“引用标志位”-
  • 每次对象被使用时,设置标志位为1
  • 新对象加入缓存时,设置其标志位为0
  • 在淘汰对象时,查看它的标志位。如果为0,则淘汰该对象;如果为1,则设置其标志位为0,重新加入队列末尾。

二、Fabric实现分析

Fabric MSP模块实现了Second-Chance算法,在目录:msp/cache

2.1 创建缓存实例

创建缓存实例时仅需设置缓存大小:

func newSecondChanceCache(cacheSize int) *secondChanceCache {
	var cache secondChanceCache
	cache.position = 0
	cache.items = make([]*cacheItem, cacheSize)
	cache.table = make(map[string]*cacheItem)

	return &cache
}

缓存实例定义为:

type secondChanceCache struct {
	// manages mapping between keys and items
	table map[string]*cacheItem

	// holds a list of cached items.
	items []*cacheItem

	// indicates the next candidate of a victim in the items list
	position int

	// read lock for get, and write lock for add
	rwlock sync.RWMutex
}

其中:

  • table:实际缓存对象的key和对象的映射
  • items:缓存,大小在初始化的时候确定
  • position :下一个淘汰检查的对象在items中的索引;初始化时指向items[0]。

2.2 查询对象

func (cache *secondChanceCache) get(key string) (interface{}, bool) {
	cache.rwlock.RLock()
	defer cache.rwlock.RUnlock()

	// 查询缓存
	item, ok := cache.table[key]
	if !ok {
		return nil, false
	}

	// 每次对象被使用时,设置标志位为1
	// referenced bit is set to true to indicate that this item is recently accessed.
	atomic.StoreInt32(&item.referenced, 1)

	// 返回缓存中的对象
	return item.value, true
}

查询的逻辑比较简单,每次对象被使用时,设置标志位为1。

2.3 插入对象

func (cache *secondChanceCache) add(key string, value interface{}) {
	cache.rwlock.Lock()
	defer cache.rwlock.Unlock()

	// 插入key值已存在的对象,则直接修改缓存中对应key的对象为新对象,并把引用次数设为1
	if old, ok := cache.table[key]; ok {
		old.value = value
		atomic.StoreInt32(&old.referenced, 1)
		return
	}

	var item cacheItem
	item.key = key
	item.value = value
	atomic.StoreInt32(&item.referenced, 0)

	size := len(cache.items)
	num := len(cache.table)
	// 缓存还未填满,则新对象直接插到items数组里
	if num < size {
		// cache is not full, so just store the new item at the end of the list
		cache.table[key] = &item
		cache.items[num] = &item
		return
	}

	// starts victim scan since cache is full
	// 缓存已经用光,则需要扫描整个items数据,进行对象的淘汰
	// 在淘汰对象时,查看它的标志位。如果为0,则淘汰该对象;如果为1,则设置其标志位为0,重新加入队列末尾。
	for {
		// checks whether this item is recently accessed or not
		victim := cache.items[cache.position]
		// 标志为0,直接淘汰此对象
		if atomic.LoadInt32(&victim.referenced) == 0 {
			// a victim is found. delete it, and store the new item here.
			delete(cache.table, victim.key)
			cache.table[key] = &item
			cache.items[cache.position] = &item
			cache.position = (cache.position + 1) % size
			return
		}

		// 标志为1,则设置其标志位为0,重新加入队列末尾(此处是修改position)。
		// referenced bit is set to false so that this item will be get purged
		// unless it is accessed until a next victim scan
		atomic.StoreInt32(&victim.referenced, 0)
		cache.position = (cache.position + 1) % size
	}
}
相关标签: 区块链 区块链