ipfs get时,blockservice首先在自己的blcokstore寻找,如果找不到,就会启动bitswap,向网络中广播wantlist,寻找block。
blockService
type blockService struct {
blockstore blockstore.Blockstore
exchange exchange.Interface
// If checkFirst is true then first check that a block doesn't
// already exist to avoid republishing the block on the exchange.
checkFirst bool
}
exchange.Interface
// Interface defines the functionality of the IPFS block exchange protocol.
type Interface interface { // type Exchanger interface
Fetcher
// TODO Should callers be concerned with whether the block was made
// available on the network?
HasBlock(blocks.Block) error
IsOnline() bool
io.Closer
}
// Fetcher is an object that can be used to retrieve blocks
type Fetcher interface {
// GetBlock returns the block associated with a given key.
GetBlock(context.Context, cid.Cid) (blocks.Block, error)
GetBlocks(context.Context, []cid.Cid) (<-chan blocks.Block, error)
}
exchange? 没错,bitswap就是用来交换块的协议。
bitswap
bitswap中,每次GetBlock都会创建一个session,session用于管理want block的过程。
// GetBlock attempts to retrieve a particular block from peers within the
// deadline enforced by the context.
func (bs *Bitswap) GetBlock(parent context.Context, k cid.Cid) (blocks.Block, error) {
return bsgetter.SyncGetBlock(parent, k, bs.GetBlocks)
}
// GetBlocks returns a channel where the caller may receive blocks that
// correspond to the provided |keys|. Returns an error if BitSwap is unable to
// begin this request within the deadline enforced by the context.
//
// NB: Your request remains open until the context expires. To conserve
// resources, provide a context with a reasonably short deadline (ie. not one
// that lasts throughout the lifetime of the server)
func (bs *Bitswap) GetBlocks(ctx context.Context, keys []cid.Cid) (<-chan blocks.Block, error) {
session := bs.sm.NewSession(ctx, bs.provSearchDelay, bs.rebroadcastDelay)
return session.GetBlocks(ctx, keys)
}
试想一下,如果get的是一个大文件,会有许多的block,如果每次都去创建一个session,这显然是一种低效的方式。
那为什么不能使用GetBlocks呢,那是因为一个大文件,需要先新建session,获取索引块,由DAG解析后,再创建session获取子块。如果子块中有索引块,又需要新建session获取子块。
自下而上
为了解决这个问题,ipfs从unixfs,dagservice和blockservice,都添加了session,也就是说,每次ipfs get就是一个session,最底层就是bitswap的session。
// Session is a helper type to provide higher level access to bitswap sessions
type Session struct {
bs blockstore.Blockstore
ses exchange.Fetcher
sessEx exchange.SessionExchange
sessCtx context.Context
lk sync.Mutex
}
func (s *Session) getSession() exchange.Fetcher {
s.lk.Lock()
defer s.lk.Unlock()
if s.ses == nil {
s.ses = s.sessEx.NewSession(s.sessCtx)
}
return s.ses
}
// GetBlock gets a block in the context of a request session
func (s *Session) GetBlock(ctx context.Context, c cid.Cid) (blocks.Block, error) {
return getBlock(ctx, c, s.bs, s.getSession) // hash security
}
// GetBlocks gets blocks in the context of a request session
func (s *Session) GetBlocks(ctx context.Context, ks []cid.Cid) <-chan blocks.Block {
return getBlocks(ctx, ks, s.bs, s.getSession) // hash security
}