bitswap wantmanager代码解读

代码版本:go-bitswap@v0.1.11

wantlist

目前bitswap代码有两处用到了wantlist,一处是engine,另外一处是wantmanager。

engine.ledgerMap中的wantlist是Wantlist类型,记录的是别的peer的wantlist。

wantmanager.wl用于记录本节点所有session的wantlist, wantmanager.bcwl用于记录本节点所有session广播的wantlist。
两者都是SessionTrackedWantlist类型的,相对Entry,sessionTrackedEntry多了一个sesTrk,用于记录那些session需要该块(key是session ID)。

// SessionTrackedWantlist is a list of wants that also track which bitswap
// sessions have requested them
type SessionTrackedWantlist struct {
	set map[cid.Cid]*sessionTrackedEntry
}

// Wantlist is a raw list of wanted blocks and their priorities
type Wantlist struct {
	set map[cid.Cid]Entry
}

// Entry is an entry in a want list, consisting of a cid and its priority
type Entry struct {
	Cid      cid.Cid
	Priority int
}

type sessionTrackedEntry struct {
	Entry
	sesTrk map[uint64]struct{}
}

本文主要集中在对wantmanager的分析。

WantManager结构

// WantManager manages a global want list. It tracks two seperate want lists -
// one for all wants, and one for wants that are specifically broadcast to the
// internet.
type WantManager struct {
	// channel requests to the run loop
	// to get predictable behavior while running this in a go routine
	// having only one channel is neccesary, so requests are processed serially
	wantMessages chan wantMessage

	// synchronized by Run loop, only touch inside there
	wl   *wantlist.SessionTrackedWantlist
	bcwl *wantlist.SessionTrackedWantlist

	ctx    context.Context
	cancel func()

	peerHandler   PeerHandler
	wantlistGauge metrics.Gauge
}

WantManager主要包含wantMessages、wl、bcwl和peerHandler。

wantMessage

wantMessage有6种消息:

wantSet
currentWantsMessage
currentBroadcastWantsMessage
wantCountMessage
connectedMessage
disconnectedMessage

其中,wantSet用于WantBlocks()和CancelWants(),这2个函数是session调用WantManager的主要接口;

currentWantsMessage和currentBroadcastWantsMessage用于获取当前的wantlist和Broadcast WantList,它们分别记录在WantManager.wl和WantManager.bcwl;

wantCountMessage用于获取当前的wantlist的大小;

connectedMessage用于通知WantManager有新的连接,wantmanager会首先把新的peer保存到wantmanager.peerHandler.peerQueues,如果已经存在,引用计数(refcnt)+1,然后向新的peer询问是否有正在brocast的wantlists(记录在bs.wantmanager.bcwl);

disconnectedMessage用于通知wantmanager某连接断开了,wantmanager会首先wantmanager.peerHandler.peerQueues.refcnt-1,然后判断refcnt是否为0,如果为0 ,delete(peerQueues, p);

wl, bcwl

wl用于记录本节点所有session的wantlist, bcwl用于记录本节点所有session广播的wantlist。

wl和bcwl都是SessionTrackedWantlist类型的,key是block CID, value是sessionTrackedEntry,前面已经提到,这里不再赘述。

// SessionTrackedWantlist is a list of wants that also track which bitswap
// sessions have requested them
type SessionTrackedWantlist struct {
	set map[cid.Cid]*sessionTrackedEntry
}

type sessionTrackedEntry struct {
	Entry
	sesTrk map[uint64]struct{}
}

peerHandler

wantmanager通过peerHandler(peermanager类型)为每个连接的peer维护一个messagequeue,messagequeue保存着sender和SessionTrackedWantlist,sender主要记录发送所用的流,sendMessage()时如果该sender不存在,则先创建连接和流。

wantmanager.peerHandler维护的peerQueues的来源有2个:

1、新连接: 当有新的连接时,libp2p swarm会通知bitswap,bitswap会通知wantmanager。比如在findprovider的过程中connect的closer peers后也会通知bitswap;

2、session通过dht找到providers,调用wantmanager.WantBlocks()将peer添加到peerQueue中。

type wantSet struct {
	entries []bsmsg.Entry
	targets []peer.ID
	from    uint64
}

func (ws *wantSet) handle(wm *WantManager) {
	// is this a broadcast or not?
	brdc := len(ws.targets) == 0

	// add changes to our wantlist
	for _, e := range ws.entries {
		if e.Cancel {
			if brdc {
				wm.bcwl.Remove(e.Cid, ws.from)
			}

			if wm.wl.Remove(e.Cid, ws.from) {
				wm.wantlistGauge.Dec()
			}
		} else {
			if brdc {
				wm.bcwl.AddEntry(e.Entry, ws.from)
			}
			if wm.wl.AddEntry(e.Entry, ws.from) {
				wm.wantlistGauge.Inc()
			}
		}
	}

	// broadcast those wantlist changes
	wm.peerHandler.SendMessage(ws.entries, ws.targets, ws.from)
}

type connectedMessage struct {
	p peer.ID
}

func (cm *connectedMessage) handle(wm *WantManager) {
	wm.peerHandler.Connected(cm.p, wm.bcwl)
}

type disconnectedMessage struct {
	p peer.ID
}

func (dm *disconnectedMessage) handle(wm *WantManager) {
	wm.peerHandler.Disconnected(dm.p)
}
peerManage

peerQueues为每个连接的peer维护一个messagequeue,

type peerQueueInstance struct {
	refcnt int
	pq     PeerQueue
}

// PeerManager manages a pool of peers and sends messages to peers in the pool.
type PeerManager struct {
	// peerQueues -- interact through internal utility functions get/set/remove/iterate
	peerQueues map[peer.ID]*peerQueueInstance

	createPeerQueue PeerQueueFactory
	ctx             context.Context
}
messagequeue

p记录连接的peerID,sender主要记录发送所用的流,wl记录对该peer的SessionTrackedWantlist。

// MessageQueue implements queue of want messages to send to peers.
type MessageQueue struct {
	ctx     context.Context
	p       peer.ID
	network MessageNetwork

	outgoingWork chan struct{}
	done         chan struct{}

	// do not touch out of run loop
	wl                    *wantlist.SessionTrackedWantlist
	nextMessage           bsmsg.BitSwapMessage
	nextMessageLk         sync.RWMutex
	sender                bsnet.MessageSender
	rebroadcastIntervalLk sync.RWMutex
	rebroadcastInterval   time.Duration
	rebroadcastTimer      *time.Timer
}

MessageSender
// ipfs_impl.go
type streamMessageSender struct {
	s     network.Stream
	bsnet *impl
}

func (s *streamMessageSender) Close() error {
	return helpers.FullClose(s.s)
}

func (s *streamMessageSender) Reset() error {
	return s.s.Reset()
}

func (s *streamMessageSender) SendMsg(ctx context.Context, msg bsmsg.BitSwapMessage) error {
	return s.bsnet.msgToStream(ctx, s.s, msg)
}

func (bsnet *impl) NewMessageSender(ctx context.Context, p peer.ID) (MessageSender, error) {
	s, err := bsnet.newStreamToPeer(ctx, p)
	if err != nil {
		return nil, err
	}

	return &streamMessageSender{s: s, bsnet: bsnet}, nil
}

如果该sender不存在,则调用Connect()创建连接,调用NewMessageSender()创建流。

func openSender(ctx context.Context, network MessageNetwork, p peer.ID) (bsnet.MessageSender, error) {
	// allow ten minutes for connections this includes looking them up in the
	// dht dialing them, and handshaking
	conctx, cancel := context.WithTimeout(ctx, time.Minute*10)
	defer cancel()

	err := network.ConnectTo(conctx, p)
	if err != nil {
		return nil, err
	}

	nsender, err := network.NewMessageSender(ctx, p)
	if err != nil {
		return nil, err
	}

	return nsender, nil
}

如果sender存在,调用SendMsg()。SendMsg()最终将消息序列化(proto)后,写到流里面。

func (mq *MessageQueue) attemptSendAndRecovery(message bsmsg.BitSwapMessage) bool {
	err := mq.sender.SendMsg(mq.ctx, message)
	if err == nil {
		return true
	}

	...
}
BitSwapMessage

虽然message中包含wantlist和blocks,也支持同时解析block和wantlist,但是两者目前是分离的,即一条消息只有wantlist或者block。

tips:

对于other peer’s wantlist, 由engine决定是否发送,最终通过bs.taskWorker()调用sendBlocks()发送出去; 对于本节点的wantlist,由wantmanager管理,最终通过messagequeue.attemptSendAndRecovery()调用SendMsg()发送出去。

对于block,发送前NewStream,发送完close;对于wantlist,messagequeue会维护已创建的stream,方便以后复用。

type impl struct {
	full     bool
	wantlist map[cid.Cid]*Entry
	blocks   map[cid.Cid]blocks.Block
}

总结

执行ipfs get命令时, ipfs首先会查找本地blockstore是否有对应的块,如果没有该块,调用NewSession,由session负责查找。

session会调用wantmanager.WantBlocks(),将wantlist添加到wantmanager.wl,wantmanager通过peerHandler(peermanager类型)为每个连接的peer维护一个messagequeue,messagequeue保存着向该peer发送的SessionTrackedWantlist,SessionTrackedWantlist的key是block CID, value是sessionTrackedEntry, session ID(作为key)保存在sessionTrackedEntry.sesTrk。

session如果收到了block,会调用wantmanager.CancelWants(),从而 delete(SessionTrackedWantlist, session ID), 表明该session不想要该block了。

wantmanager是在new bitswap的时候被实例化,并且代入bitswap.wm,以后newsession时作为参数传入到session中。也就是说wantmanager管理的是本节点所有session的wantlist。

值得注意的是,wantmanager是被动的,机械的,只有WantBlocks()、CancelWants()以及Connected()、Disconnected()被调用才会运转,前三者都会向与之连接的peer发送wantlist请求。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值