github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/swarm/storage/chunker.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 19:16:44</date>
    10  //</624450118287888384>
    11  
    12  package storage
    13  
    14  import (
    15  	"context"
    16  	"encoding/binary"
    17  	"errors"
    18  	"fmt"
    19  	"io"
    20  	"sync"
    21  	"time"
    22  
    23  	"github.com/ethereum/go-ethereum/metrics"
    24  	ch "github.com/ethereum/go-ethereum/swarm/chunk"
    25  	"github.com/ethereum/go-ethereum/swarm/log"
    26  	"github.com/ethereum/go-ethereum/swarm/spancontext"
    27  	opentracing "github.com/opentracing/opentracing-go"
    28  	olog "github.com/opentracing/opentracing-go/log"
    29  )
    30  
    31  /*
    32  此包中实现的分布式存储需要固定大小的内容块。
    33  
    34  chunker是一个组件的接口,该组件负责分解和组装较大的数据。
    35  
    36  TreeChunker基于树结构实现一个chunker,定义如下:
    37  
    38  1树中的每个节点(包括根节点和其他分支节点)都存储为一个块。
    39  
    40  2个分支节点对数据内容进行编码,这些内容包括节点下的整个子树所覆盖的数据切片大小及其所有子节点的哈希键:
    41  数据i:=大小(子树i)键j键j+1…..Ki{{J+N-1}
    42  
    43  3个叶节点对输入数据的实际子片进行编码。
    44  
    45  4如果数据大小不超过最大chunk size,则数据存储在单个块中。
    46    键=哈希(Int64(大小)+数据)
    47  
    48  5如果数据大小大于chunksize*分支^l,但不大于chunksize*
    49    分支^(L+1),数据向量被分割成块大小*
    50    分支长度(最后一个除外)。
    51    key=hash(int64(大小)+key(slice0)+key(slice1)+…)
    52  
    53   底层哈希函数是可配置的
    54  **/
    55  
    56  
    57  /*
    58  树分块器是数据分块的具体实现。
    59  这个chunker以一种简单的方式工作,它从文档中构建一个树,这样每个节点要么表示一块实际数据,要么表示一块表示树的分支非叶节点的数据。尤其是,每个这样的非叶块将表示其各自子块的散列的串联。该方案同时保证了数据的完整性和自寻址能力。抽象节点是透明的,因为其表示的大小组件严格大于其最大数据大小,因为它们编码子树。
    60  
    61  如果一切正常,可以通过简单地编写读卡器来实现这一点,这样就不需要额外的分配或缓冲来进行数据拆分和连接。这意味着原则上,内存、文件系统、网络套接字之间可以有直接的IO(BZZ对等机存储请求是从套接字读取的)。实际上,可能需要几个阶段的内部缓冲。
    62  不过,散列本身确实使用了额外的副本和分配,因为它确实需要它。
    63  **/
    64  
    65  
    66  type ChunkerParams struct {
    67  	chunkSize int64
    68  	hashSize  int64
    69  }
    70  
    71  type SplitterParams struct {
    72  	ChunkerParams
    73  	reader io.Reader
    74  	putter Putter
    75  	addr   Address
    76  }
    77  
    78  type TreeSplitterParams struct {
    79  	SplitterParams
    80  	size int64
    81  }
    82  
    83  type JoinerParams struct {
    84  	ChunkerParams
    85  	addr   Address
    86  	getter Getter
    87  //TODO:有一个bug,所以深度今天只能是0,请参见:https://github.com/ethersphere/go-ethereum/issues/344
    88  	depth int
    89  	ctx   context.Context
    90  }
    91  
    92  type TreeChunker struct {
    93  	ctx context.Context
    94  
    95  	branches int64
    96  	dataSize int64
    97  	data     io.Reader
    98  //计算
    99  	addr        Address
   100  	depth       int
   101  hashSize    int64        //self.hashfunc.new().size()。
   102  chunkSize   int64        //哈希大小*分支
   103  workerCount int64        //使用的工作程序数
   104  workerLock  sync.RWMutex //锁定工人计数
   105  	jobC        chan *hashJob
   106  	wg          *sync.WaitGroup
   107  	putter      Putter
   108  	getter      Getter
   109  	errC        chan error
   110  	quitC       chan bool
   111  }
   112  
   113  /*
   114   join基于根键重新构造原始内容。
   115   加入时,调用方将返回一个惰性的区段读取器,即
   116   可查找并实现按需获取块以及读取块的位置。
   117   要检索的新块来自调用方提供的getter。
   118   如果在联接过程中遇到错误,则显示为读卡器错误。
   119   区段阅读器。
   120   因此,即使其他部分也可以部分读取文档
   121   损坏或丢失。
   122   块在加入时不会被chunker验证。这个
   123   是因为由DPA决定哪些来源是可信的。
   124  **/
   125  
   126  func TreeJoin(ctx context.Context, addr Address, getter Getter, depth int) *LazyChunkReader {
   127  	jp := &JoinerParams{
   128  		ChunkerParams: ChunkerParams{
   129  			chunkSize: ch.DefaultSize,
   130  			hashSize:  int64(len(addr)),
   131  		},
   132  		addr:   addr,
   133  		getter: getter,
   134  		depth:  depth,
   135  		ctx:    ctx,
   136  	}
   137  
   138  	return NewTreeJoiner(jp).Join(ctx)
   139  }
   140  
   141  /*
   142   拆分时,数据作为一个节阅读器提供,键是一个哈希大小的长字节片(键),一旦处理完成,整个内容的根哈希将填充此内容。
   143   要存储的新块是使用调用方提供的推杆存储的。
   144  **/
   145  
   146  func TreeSplit(ctx context.Context, data io.Reader, size int64, putter Putter) (k Address, wait func(context.Context) error, err error) {
   147  	tsp := &TreeSplitterParams{
   148  		SplitterParams: SplitterParams{
   149  			ChunkerParams: ChunkerParams{
   150  				chunkSize: ch.DefaultSize,
   151  				hashSize:  putter.RefSize(),
   152  			},
   153  			reader: data,
   154  			putter: putter,
   155  		},
   156  		size: size,
   157  	}
   158  	return NewTreeSplitter(tsp).Split(ctx)
   159  }
   160  
   161  func NewTreeJoiner(params *JoinerParams) *TreeChunker {
   162  	tc := &TreeChunker{}
   163  	tc.hashSize = params.hashSize
   164  	tc.branches = params.chunkSize / params.hashSize
   165  	tc.addr = params.addr
   166  	tc.getter = params.getter
   167  	tc.depth = params.depth
   168  	tc.chunkSize = params.chunkSize
   169  	tc.workerCount = 0
   170  	tc.jobC = make(chan *hashJob, 2*ChunkProcessors)
   171  	tc.wg = &sync.WaitGroup{}
   172  	tc.errC = make(chan error)
   173  	tc.quitC = make(chan bool)
   174  
   175  	tc.ctx = params.ctx
   176  
   177  	return tc
   178  }
   179  
   180  func NewTreeSplitter(params *TreeSplitterParams) *TreeChunker {
   181  	tc := &TreeChunker{}
   182  	tc.data = params.reader
   183  	tc.dataSize = params.size
   184  	tc.hashSize = params.hashSize
   185  	tc.branches = params.chunkSize / params.hashSize
   186  	tc.addr = params.addr
   187  	tc.chunkSize = params.chunkSize
   188  	tc.putter = params.putter
   189  	tc.workerCount = 0
   190  	tc.jobC = make(chan *hashJob, 2*ChunkProcessors)
   191  	tc.wg = &sync.WaitGroup{}
   192  	tc.errC = make(chan error)
   193  	tc.quitC = make(chan bool)
   194  
   195  	return tc
   196  }
   197  
   198  type hashJob struct {
   199  	key      Address
   200  	chunk    []byte
   201  	size     int64
   202  	parentWg *sync.WaitGroup
   203  }
   204  
   205  func (tc *TreeChunker) incrementWorkerCount() {
   206  	tc.workerLock.Lock()
   207  	defer tc.workerLock.Unlock()
   208  	tc.workerCount += 1
   209  }
   210  
   211  func (tc *TreeChunker) getWorkerCount() int64 {
   212  	tc.workerLock.RLock()
   213  	defer tc.workerLock.RUnlock()
   214  	return tc.workerCount
   215  }
   216  
   217  func (tc *TreeChunker) decrementWorkerCount() {
   218  	tc.workerLock.Lock()
   219  	defer tc.workerLock.Unlock()
   220  	tc.workerCount -= 1
   221  }
   222  
   223  func (tc *TreeChunker) Split(ctx context.Context) (k Address, wait func(context.Context) error, err error) {
   224  	if tc.chunkSize <= 0 {
   225  		panic("chunker must be initialised")
   226  	}
   227  
   228  	tc.runWorker(ctx)
   229  
   230  	depth := 0
   231  	treeSize := tc.chunkSize
   232  
   233  //取最小深度,使chunkSize*hashCount^(depth+1)>size
   234  //幂级数,将找出数据大小在基散列计数中的数量级或结果树中分支级别的数量级。
   235  	for ; treeSize < tc.dataSize; treeSize *= tc.branches {
   236  		depth++
   237  	}
   238  
   239  	key := make([]byte, tc.hashSize)
   240  //此waitgroup成员在计算根哈希之后释放
   241  	tc.wg.Add(1)
   242  //启动传递等待组的实际递归函数
   243  	go tc.split(ctx, depth, treeSize/tc.branches, key, tc.dataSize, tc.wg)
   244  
   245  //如果工作组中的所有子进程都已完成,则关闭内部错误通道
   246  	go func() {
   247  //等待所有线程完成
   248  		tc.wg.Wait()
   249  		close(tc.errC)
   250  	}()
   251  
   252  	defer close(tc.quitC)
   253  	defer tc.putter.Close()
   254  	select {
   255  	case err := <-tc.errC:
   256  		if err != nil {
   257  			return nil, nil, err
   258  		}
   259  	case <-ctx.Done():
   260  		return nil, nil, ctx.Err()
   261  	}
   262  
   263  	return key, tc.putter.Wait, nil
   264  }
   265  
   266  func (tc *TreeChunker) split(ctx context.Context, depth int, treeSize int64, addr Address, size int64, parentWg *sync.WaitGroup) {
   267  
   268  //
   269  
   270  	for depth > 0 && size < treeSize {
   271  		treeSize /= tc.branches
   272  		depth--
   273  	}
   274  
   275  	if depth == 0 {
   276  //叶节点->内容块
   277  		chunkData := make([]byte, size+8)
   278  		binary.LittleEndian.PutUint64(chunkData[0:8], uint64(size))
   279  		var readBytes int64
   280  		for readBytes < size {
   281  			n, err := tc.data.Read(chunkData[8+readBytes:])
   282  			readBytes += int64(n)
   283  			if err != nil && !(err == io.EOF && readBytes == size) {
   284  				tc.errC <- err
   285  				return
   286  			}
   287  		}
   288  		select {
   289  		case tc.jobC <- &hashJob{addr, chunkData, size, parentWg}:
   290  		case <-tc.quitC:
   291  		}
   292  		return
   293  	}
   294  //部门> 0
   295  //包含子节点哈希的中间块
   296  	branchCnt := (size + treeSize - 1) / treeSize
   297  
   298  	var chunk = make([]byte, branchCnt*tc.hashSize+8)
   299  	var pos, i int64
   300  
   301  	binary.LittleEndian.PutUint64(chunk[0:8], uint64(size))
   302  
   303  	childrenWg := &sync.WaitGroup{}
   304  	var secSize int64
   305  	for i < branchCnt {
   306  //最后一项可以有较短的数据
   307  		if size-pos < treeSize {
   308  			secSize = size - pos
   309  		} else {
   310  			secSize = treeSize
   311  		}
   312  //数据的散列值
   313  		subTreeAddress := chunk[8+i*tc.hashSize : 8+(i+1)*tc.hashSize]
   314  
   315  		childrenWg.Add(1)
   316  		tc.split(ctx, depth-1, treeSize/tc.branches, subTreeAddress, secSize, childrenWg)
   317  
   318  		i++
   319  		pos += treeSize
   320  	}
   321  //等待所有子元素完成哈希计算并将其复制到块的各个部分
   322  //添加(1)
   323  //转到函数()
   324  	childrenWg.Wait()
   325  
   326  	worker := tc.getWorkerCount()
   327  	if int64(len(tc.jobC)) > worker && worker < ChunkProcessors {
   328  		tc.runWorker(ctx)
   329  
   330  	}
   331  	select {
   332  	case tc.jobC <- &hashJob{addr, chunk, size, parentWg}:
   333  	case <-tc.quitC:
   334  	}
   335  }
   336  
   337  func (tc *TreeChunker) runWorker(ctx context.Context) {
   338  	tc.incrementWorkerCount()
   339  	go func() {
   340  		defer tc.decrementWorkerCount()
   341  		for {
   342  			select {
   343  
   344  			case job, ok := <-tc.jobC:
   345  				if !ok {
   346  					return
   347  				}
   348  
   349  				h, err := tc.putter.Put(ctx, job.chunk)
   350  				if err != nil {
   351  					tc.errC <- err
   352  					return
   353  				}
   354  				copy(job.key, h)
   355  				job.parentWg.Done()
   356  			case <-tc.quitC:
   357  				return
   358  			}
   359  		}
   360  	}()
   361  }
   362  
   363  //Lazychunkreader实现LazySectionReader
   364  type LazyChunkReader struct {
   365  	ctx       context.Context
   366  addr      Address //根地址
   367  	chunkData ChunkData
   368  off       int64 //抵消
   369  chunkSize int64 //从Chunker继承
   370  branches  int64 //从Chunker继承
   371  hashSize  int64 //从Chunker继承
   372  	depth     int
   373  	getter    Getter
   374  }
   375  
   376  func (tc *TreeChunker) Join(ctx context.Context) *LazyChunkReader {
   377  	return &LazyChunkReader{
   378  		addr:      tc.addr,
   379  		chunkSize: tc.chunkSize,
   380  		branches:  tc.branches,
   381  		hashSize:  tc.hashSize,
   382  		depth:     tc.depth,
   383  		getter:    tc.getter,
   384  		ctx:       tc.ctx,
   385  	}
   386  }
   387  
   388  func (r *LazyChunkReader) Context() context.Context {
   389  	return r.ctx
   390  }
   391  
   392  //大小将在LazySectionReader上调用
   393  func (r *LazyChunkReader) Size(ctx context.Context, quitC chan bool) (n int64, err error) {
   394  	metrics.GetOrRegisterCounter("lazychunkreader.size", nil).Inc(1)
   395  
   396  	var sp opentracing.Span
   397  	var cctx context.Context
   398  	cctx, sp = spancontext.StartSpan(
   399  		ctx,
   400  		"lcr.size")
   401  	defer sp.Finish()
   402  
   403  	log.Debug("lazychunkreader.size", "addr", r.addr)
   404  	if r.chunkData == nil {
   405  		startTime := time.Now()
   406  		chunkData, err := r.getter.Get(cctx, Reference(r.addr))
   407  		if err != nil {
   408  			metrics.GetOrRegisterResettingTimer("lcr.getter.get.err", nil).UpdateSince(startTime)
   409  			return 0, err
   410  		}
   411  		metrics.GetOrRegisterResettingTimer("lcr.getter.get", nil).UpdateSince(startTime)
   412  		r.chunkData = chunkData
   413  	}
   414  
   415  	s := r.chunkData.Size()
   416  	log.Debug("lazychunkreader.size", "key", r.addr, "size", s)
   417  
   418  	return int64(s), nil
   419  }
   420  
   421  //读在可以被称为无数次
   422  //允许并发读取
   423  //首先需要在Lazychunkreader上同步调用size()。
   424  func (r *LazyChunkReader) ReadAt(b []byte, off int64) (read int, err error) {
   425  	metrics.GetOrRegisterCounter("lazychunkreader.readat", nil).Inc(1)
   426  
   427  	var sp opentracing.Span
   428  	var cctx context.Context
   429  	cctx, sp = spancontext.StartSpan(
   430  		r.ctx,
   431  		"lcr.read")
   432  	defer sp.Finish()
   433  
   434  	defer func() {
   435  		sp.LogFields(
   436  			olog.Int("off", int(off)),
   437  			olog.Int("read", read))
   438  	}()
   439  
   440  //这是正确的,swarm文档不能是零长度,因此不需要EOF
   441  	if len(b) == 0 {
   442  		return 0, nil
   443  	}
   444  	quitC := make(chan bool)
   445  	size, err := r.Size(cctx, quitC)
   446  	if err != nil {
   447  		log.Debug("lazychunkreader.readat.size", "size", size, "err", err)
   448  		return 0, err
   449  	}
   450  
   451  	errC := make(chan error)
   452  
   453  //}
   454  	var treeSize int64
   455  	var depth int
   456  //计算深度和最大树尺寸
   457  	treeSize = r.chunkSize
   458  	for ; treeSize < size; treeSize *= r.branches {
   459  		depth++
   460  	}
   461  	wg := sync.WaitGroup{}
   462  	length := int64(len(b))
   463  	for d := 0; d < r.depth; d++ {
   464  		off *= r.chunkSize
   465  		length *= r.chunkSize
   466  	}
   467  	wg.Add(1)
   468  	go r.join(b, off, off+length, depth, treeSize/r.branches, r.chunkData, &wg, errC, quitC)
   469  	go func() {
   470  		wg.Wait()
   471  		close(errC)
   472  	}()
   473  
   474  	err = <-errC
   475  	if err != nil {
   476  		log.Debug("lazychunkreader.readat.errc", "err", err)
   477  		close(quitC)
   478  		return 0, err
   479  	}
   480  	if off+int64(len(b)) >= size {
   481  		log.Debug("lazychunkreader.readat.return at end", "size", size, "off", off)
   482  		return int(size - off), io.EOF
   483  	}
   484  	log.Debug("lazychunkreader.readat.errc", "buff", len(b))
   485  	return len(b), nil
   486  }
   487  
   488  func (r *LazyChunkReader) join(b []byte, off int64, eoff int64, depth int, treeSize int64, chunkData ChunkData, parentWg *sync.WaitGroup, errC chan error, quitC chan bool) {
   489  	defer parentWg.Done()
   490  //查找适当的块级别
   491  	for chunkData.Size() < uint64(treeSize) && depth > r.depth {
   492  		treeSize /= r.branches
   493  		depth--
   494  	}
   495  
   496  //找到叶块
   497  	if depth == r.depth {
   498  		extra := 8 + eoff - int64(len(chunkData))
   499  		if extra > 0 {
   500  			eoff -= extra
   501  		}
   502  		copy(b, chunkData[8+off:8+eoff])
   503  return //只需将内容块返回给块阅读器
   504  	}
   505  
   506  //子树
   507  	start := off / treeSize
   508  	end := (eoff + treeSize - 1) / treeSize
   509  
   510  //最后一个非叶块可以短于默认块大小,我们不要再进一步读取它的结尾
   511  	currentBranches := int64(len(chunkData)-8) / r.hashSize
   512  	if end > currentBranches {
   513  		end = currentBranches
   514  	}
   515  
   516  	wg := &sync.WaitGroup{}
   517  	defer wg.Wait()
   518  	for i := start; i < end; i++ {
   519  		soff := i * treeSize
   520  		roff := soff
   521  		seoff := soff + treeSize
   522  
   523  		if soff < off {
   524  			soff = off
   525  		}
   526  		if seoff > eoff {
   527  			seoff = eoff
   528  		}
   529  		if depth > 1 {
   530  			wg.Wait()
   531  		}
   532  		wg.Add(1)
   533  		go func(j int64) {
   534  			childAddress := chunkData[8+j*r.hashSize : 8+(j+1)*r.hashSize]
   535  			startTime := time.Now()
   536  			chunkData, err := r.getter.Get(r.ctx, Reference(childAddress))
   537  			if err != nil {
   538  				metrics.GetOrRegisterResettingTimer("lcr.getter.get.err", nil).UpdateSince(startTime)
   539  				log.Debug("lazychunkreader.join", "key", fmt.Sprintf("%x", childAddress), "err", err)
   540  				select {
   541  				case errC <- fmt.Errorf("chunk %v-%v not found; key: %s", off, off+treeSize, fmt.Sprintf("%x", childAddress)):
   542  				case <-quitC:
   543  				}
   544  				return
   545  			}
   546  			metrics.GetOrRegisterResettingTimer("lcr.getter.get", nil).UpdateSince(startTime)
   547  			if l := len(chunkData); l < 9 {
   548  				select {
   549  				case errC <- fmt.Errorf("chunk %v-%v incomplete; key: %s, data length %v", off, off+treeSize, fmt.Sprintf("%x", childAddress), l):
   550  				case <-quitC:
   551  				}
   552  				return
   553  			}
   554  			if soff < off {
   555  				soff = off
   556  			}
   557  			r.join(b[soff-off:seoff-off], soff-roff, seoff-roff, depth-1, treeSize/r.branches, chunkData, wg, errC, quitC)
   558  		}(i)
   559  } //对于
   560  }
   561  
   562  //read保留一个光标,因此不能同时调用,请参阅readat
   563  func (r *LazyChunkReader) Read(b []byte) (read int, err error) {
   564  	log.Debug("lazychunkreader.read", "key", r.addr)
   565  	metrics.GetOrRegisterCounter("lazychunkreader.read", nil).Inc(1)
   566  
   567  	read, err = r.ReadAt(b, r.off)
   568  	if err != nil && err != io.EOF {
   569  		log.Debug("lazychunkreader.readat", "read", read, "err", err)
   570  		metrics.GetOrRegisterCounter("lazychunkreader.read.err", nil).Inc(1)
   571  	}
   572  
   573  	metrics.GetOrRegisterCounter("lazychunkreader.read.bytes", nil).Inc(int64(read))
   574  
   575  	r.off += int64(read)
   576  	return read, err
   577  }
   578  
   579  //完全类似于标准的sectionreader实现
   580  var errWhence = errors.New("Seek: invalid whence")
   581  var errOffset = errors.New("Seek: invalid offset")
   582  
   583  func (r *LazyChunkReader) Seek(offset int64, whence int) (int64, error) {
   584  	log.Debug("lazychunkreader.seek", "key", r.addr, "offset", offset)
   585  	switch whence {
   586  	default:
   587  		return 0, errWhence
   588  	case 0:
   589  		offset += 0
   590  	case 1:
   591  		offset += r.off
   592  	case 2:
   593  if r.chunkData == nil { //从结尾搜索要求rootchunk的大小。先调用大小
   594  			_, err := r.Size(context.TODO(), nil)
   595  			if err != nil {
   596  				return 0, fmt.Errorf("can't get size: %v", err)
   597  			}
   598  		}
   599  		offset += int64(r.chunkData.Size())
   600  	}
   601  
   602  	if offset < 0 {
   603  		return 0, errOffset
   604  	}
   605  	r.off = offset
   606  	return offset, nil
   607  }
   608