ipfs, bitswap architecture

Block diagram

Copied from github go-bitswap repo

bitswap data sturcure & initialization

// Bitswap instances implement the bitswap protocol.
type Bitswap struct {
	// the wantlist tracks global wants for bitswap
	wm *bswm.WantManager
	// the provider query manager manages requests to find providers
	pqm *bspqm.ProviderQueryManager
	// the engine is the bit of logic that decides who to send which blocks to
	engine *decision.Engine
	// network delivers messages on behalf of the session
	network bsnet.BitSwapNetwork
	// blockstore is the local database
	// NB: ensure threadsafety
	blockstore blockstore.Blockstore
	// manages channels of outgoing blocks for sessions
	notif notifications.PubSub
	// newBlocks is a channel for newly added blocks to be provided to the
	// network.  blocks pushed down this channel get buffered and fed to the
	// provideKeys channel later on to avoid too much network activity
	newBlocks chan cid.Cid
	// provideKeys directly feeds provide workers
	provideKeys chan cid.Cid
	// the sessionmanager manages tracking sessions
	sm *bssm.SessionManager
	...
}

wm -> wantManager
sm -> SessionManager
pqm -> ProviderQueryManager

When initializing bitswap, wm, pqm, bs.workers, decisionEngine workers will be started as well. Moreover, provideCollector and ProvideWorker will be started when ProviderEnable is true.

	bs.wm.Startup()
	bs.pqm.Startup()
	network.SetDelegate(bs)

	// Start up bitswaps async worker routines
	bs.startWorkers(ctx, px)
	engine.StartWorkers(ctx, px)

	// bind the context and process.
	// do it over here to avoid closing before all setup is done.
	go func() {
		<-px.Closing() // process closes first
		cancelFunc()
		notif.Shutdown()
	}()

bitswap Worker

Waiting for bs.engine.Outbox(), then extracting Message/Blocks, finnally bs.sendBlocks(ctx, envelope)…


func (bs *Bitswap) taskWorker(ctx context.Context, id int) {
	for {
		select {
		case nextEnvelope := <-bs.engine.Outbox():
			select {
			case envelope, ok := <-nextEnvelope:
				if !ok {
					continue
				}
				// update the BS ledger to reflect sent message
				// TODO: Should only track *useful* messages in ledger
				outgoing := bsmsg.New(false)
				for _, block := range envelope.Message.Blocks() {
					outgoing.AddBlock(block)
				}
				bs.engine.MessageSent(envelope.Peer, outgoing)
			case <-ctx.Done():
				return
			}
		case <-ctx.Done():
			return
		}
	}
}

WantManager

Simple, the task of wm is shown below. It will take message from wm/wantMessages.

func (wm *WantManager) run() {
	// NOTE: Do not open any streams or connections from anywhere in this
	// event loop. Really, just don't do anything likely to block.
	for {
		select {
		case message := <-wm.wantMessages:
			message.handle(wm)
		case <-wm.ctx.Done():
			return
		}
	}
}

Availble messages:

  • wantSet
  • currentBroadcastWantsMessage
  • currentWantsMessage
  • wantCountMessage
  • connectedMessage
  • disconnectedMessage

Providing

  • ProvideCollector
  • ProvideWorker

SessionManager & Session

SessionManager is responsible for managing session’s lifecycle, NewSession/removeSession.

A session will be created by bitswap.NewSession(), in case that we do some block requests.


// Session holds state for an individual bitswap transfer operation.
// This allows bitswap to make smarter decisions about who to send wantlist
// info to, and who to request blocks from.
type Session struct {
	// dependencies
	ctx context.Context
	wm  WantManager
	pm  PeerManager
	srs RequestSplitter

	sw sessionWants

	// channels
	incoming      chan op
	latencyReqs   chan chan time.Duration
	tickDelayReqs chan time.Duration

	// do not touch outside run loop
	idleTick            *time.Timer
	periodicSearchTimer *time.Timer
	baseTickDelay       time.Duration
	latTotal            time.Duration
	fetchcnt            int
	consecutiveTicks    int
	initialSearchDelay  time.Duration
	periodicSearchDelay delay.D
	// identifiers
	notif notifications.PubSub
	uuid  logging.Loggable
	id    uint64
}

type SessionManager struct {
	ctx                    context.Context
	sessionFactory         SessionFactory
	peerManagerFactory     PeerManagerFactory
	requestSplitterFactory RequestSplitterFactory
	notif                  notifications.PubSub

	// Sessions
	sessLk   sync.RWMutex
	sessions []sesTrk

	// Session Index
	sessIDLk sync.Mutex
	sessID   uint64
}

peerManagerFactory & requestSplitterFactory are used in NewSession. So, whenever a session is created, a peerManager and a requestSplitter will be created as well. All together, session, peerMgr and splitter will grouped as a tracked Session.

Also, a routine is created to safe guard the context cancellation.

// NewSession initializes a session with the given context, and adds to the
// session manager.
func (sm *SessionManager) NewSession(ctx context.Context,
	provSearchDelay time.Duration,
	rebroadcastDelay delay.D) exchange.Fetcher {
	id := sm.GetNextSessionID()
	sessionctx, cancel := context.WithCancel(ctx)

	pm := sm.peerManagerFactory(sessionctx, id)
	srs := sm.requestSplitterFactory(sessionctx)
	session := sm.sessionFactory(sessionctx, id, pm, srs, sm.notif, provSearchDelay, rebroadcastDelay)
	tracked := sesTrk{session, pm, srs}
	sm.sessLk.Lock()
	sm.sessions = append(sm.sessions, tracked)
	sm.sessLk.Unlock()
	go func() {
		defer cancel()
		select {
		case <-sm.ctx.Done():
			sm.removeSession(tracked)
		case <-ctx.Done():
			sm.removeSession(tracked)
		}
	}()
	return session
}

SessionPeerManager

A session will create a PeerManager for managing peers.

// SessionPeerManager tracks and manages peers for a session, and provides
// the best ones to the session
type SessionPeerManager struct {
	ctx            context.Context
	tagger         PeerTagger
	providerFinder PeerProviderFinder
	tag            string
	id             uint64

	peerMessages chan peerMessage

	// do not touch outside of run loop
	activePeers         map[peer.ID]*peerData
	unoptimizedPeersArr []peer.ID
	optimizedPeersArr   []peer.ID
	broadcastLatency    *latencyTracker
	timeoutDuration     time.Duration
}

SessionPeerManager will start a loop to receiving message:

func (spm *SessionPeerManager) run(ctx context.Context) {
	for {
		select {
		case pm := <-spm.peerMessages:
			pm.handle(spm)
		case <-ctx.Done():
			spm.handleShutdown()
			return
		}
	}
}

Message types:

  • peerFoundMessage
  • peerResponseMessage
  • peerRequestMessage
  • getPeersMessage
  • cancelMessage
  • peerTimeoutMessage
  • broadcastTimeoutMessage
  • setTimeoutMessage

peerManagerFactory & requestSplitterFactory

Actually these two look like this:

sessionPeerManagerFactory := func(ctx context.Context, id uint64) bssession.PeerManager {
	return bsspm.New(ctx, id, network.ConnectionManager(), pqm)
}
sessionRequestSplitterFactory := func(ctx context.Context) bssession.RequestSplitter {
	return bssrs.New(ctx)
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章