ITPub博客

首页 > 区块链 > 区块链 > 区块链教程以太坊源码分析chain-indexer区块链索引一

区块链教程以太坊源码分析chain-indexer区块链索引一

原创 区块链 作者:兄弟连区块链入门教程 时间:2018-10-19 10:26:12 0 删除 编辑

兄弟连区块链教程以太坊源码分析chain-indexer区块链索引一

chain_indexer 区块链索引

chain_indexer.go 源码解析

chain_indexer 顾名思义, 就是用来给区块链创建索引的功能。 之前在eth协议的时候,介绍过BloomIndexer的功能,其实BloomIndexer是chain_indexer的一个特殊的实现, 可以理解为派生类, 主要的功能其实实在chain_indexer这里面实现的。虽说是派生类,但是chain_indexer其实就只被BloomIndexer使用。也就是给区块链的布隆过滤器创建了索引,以便快速的响应用户的日志搜索功能。 下面就来分析这部分的代码。

数据结构

 

  1. // ChainIndexerBackend defines the methods needed to process chain segments in

  2. // the background and write the segment results into the database. These can be

  3. // used to create filter blooms or CHTs.

  4. // ChainIndexerBackend定义了处理区块链片段的方法,并把处理结果写入数据库。 这些可以用来创建布隆过滤器或者CHTs.

  5. // BloomIndexer 其实就是实现了这个接口 ChainIndexerBackend 这里的CHTs不知道是什么东西。

  6. type ChainIndexerBackend interface {

  7. // Reset initiates the processing of a new chain segment, potentially terminating

  8. // any partially completed operations (in case of a reorg).

  9. // Reset 方法用来初始化一个新的区块链片段,可能会终止任何没有完成的操作。

  10. Reset(section uint64)

  11.  

  12. // Process crunches through the next header in the chain segment. The caller

  13. // will ensure a sequential order of headers.

  14. // 对区块链片段中的下一个区块头进行处理。 调用者将确保区块头的连续顺序。

  15. Process(header *types.Header)

  16.  

  17. // Commit finalizes the section metadata and stores it into the database.

  18. 完成区块链片段的元数据并将其存储到数据库中。

  19. Commit() error

  20. }

  21.  

  22. // ChainIndexer does a post-processing job for equally sized sections of the

  23. // canonical chain (like BlooomBits and CHT structures). A ChainIndexer is

  24. // connected to the blockchain through the event system by starting a

  25. // ChainEventLoop in a goroutine.

  26. // ChainIndexer 对区块链进行 大小相等的片段 进行处。 ChainIndexer在ChainEventLoop方法中通过事件系统与区块链通信,

  27. // Further child ChainIndexers can be added which use the output of the parent

  28. // section indexer. These child indexers receive new head notifications only

  29. // after an entire section has been finished or in case of rollbacks that might

  30. // affect already finished sections.

  31. //更远可以添加使用父section索引器的输出的更多子链式索引器。 这些子链式索引器只有在整个部分完成后或在可能影响已完成部分的回滚的情况下才接收新的头部通知。

  32.  

  33. type ChainIndexer struct {

  34. chainDb ethdb.Database // Chain database to index the data from 区块链所在的数据库

  35. indexDb ethdb.Database // Prefixed table-view of the db to write index metadata into 索引存储的数据库

  36. backend ChainIndexerBackend // Background processor generating the index data content 索引生成的后端。

  37. children []*ChainIndexer // Child indexers to cascade chain updates to 子索引

  38.  

  39. active uint32 // Flag whether the event loop was started

  40. update chan struct{} // Notification channel that headers should be processed 接收到的headers

  41. quit chan chan error // Quit channel to tear down running goroutines

  42.  

  43. sectionSize uint64 // Number of blocks in a single chain segment to process section的大小。 默认是4096个区块为一个section

  44. confirmsReq uint64 // Number of confirmations before processing a completed segment 处理完成的段之前的确认次数

  45.  

  46. storedSections uint64 // Number of sections successfully indexed into the database 成功索引到数据库的部分数量

  47. knownSections uint64 // Number of sections known to be complete (block wise) 已知完成的部分数量

  48. cascadedHead uint64 // Block number of the last completed section cascaded to subindexers 级联到子索引的最后一个完成部分的块号

  49.  

  50. throttling time.Duration // Disk throttling to prevent a heavy upgrade from hogging resources 磁盘限制,以防止大量资源的大量升级

  51.  

  52. log log.Logger

  53. lock sync.RWMutex

  54. }

构造函数NewChainIndexer,

 

  1. 这个方法是在eth/bloombits.go里面被调用的

  2. const (

  3. // bloomConfirms is the number of confirmation blocks before a bloom section is

  4. // considered probably final and its rotated bits are calculated.

  5. // bloomConfirms 用来表示确认区块数量, 表示经过这么多区块之后, bloom section被认为是已经不会更改了。

  6. bloomConfirms = 256

  7.  

  8. // bloomThrottling is the time to wait between processing two consecutive index

  9. // sections. It's useful during chain upgrades to prevent disk overload.

  10. // bloomThrottling是处理两个连续索引段之间的等待时间。 在区块链升级过程中防止磁盘过载是很有用的。

  11. bloomThrottling = 100 * time.Millisecond

  12. )

  13.  

  14. func NewBloomIndexer(db ethdb.Database, size uint64) *core.ChainIndexer {

  15. backend := &BloomIndexer{

  16. db: db,

  17. size: size,

  18. }

  19. // 可以看到indexDb和chainDb实际是同一个数据库, 但是indexDb的每个key前面附加了一个BloomBitsIndexPrefix的前缀。

  20. table := ethdb.NewTable(db, string(core.BloomBitsIndexPrefix))

  21.  

  22. return core.NewChainIndexer(db, table, backend, size, bloomConfirms, bloomThrottling, "bloombits")

  23. }

  24.  

  25.  

  26. // NewChainIndexer creates a new chain indexer to do background processing on

  27. // chain segments of a given size after certain number of confirmations passed.

  28. // The throttling parameter might be used to prevent database thrashing.

  29.  

  30. func NewChainIndexer(chainDb, indexDb ethdb.Database, backend ChainIndexerBackend, section, confirm uint64, throttling time.Duration, kind string) *ChainIndexer {

  31. c := &ChainIndexer{

  32. chainDb: chainDb,

  33. indexDb: indexDb,

  34. backend: backend,

  35. update: make(chan struct{}, 1),

  36. quit: make(chan chan error),

  37. sectionSize: section,

  38. confirmsReq: confirm,

  39. throttling: throttling,

  40. log: log.New("type", kind),

  41. }

  42. // Initialize database dependent fields and start the updater

  43. c.loadValidSections()

  44. go c.updateLoop()

  45.  

  46. return c

  47. }

loadValidSections,用来从数据库里面加载我们之前的处理信息, storedSections表示我们已经处理到哪里了。

 

  1. // loadValidSections reads the number of valid sections from the index database

  2. // and caches is into the local state.

  3. func (c *ChainIndexer) loadValidSections() {

  4. data, _ := c.indexDb.Get([]byte("count"))

  5. if len(data) == 8 {

  6. c.storedSections = binary.BigEndian.Uint64(data[:])

  7. }

  8. }

updateLoop,是主要的事件循环,用于调用backend来处理区块链section,这个需要注意的是,所有的主索引节点和所有的 child indexer 都会启动这个goroutine 方法。

 

  1. func (c *ChainIndexer) updateLoop() {

  2. var (

  3. updating bool

  4. updated time.Time

  5. )

  6. for {

  7. select {

  8. case errc := c.storedSections { // 如果当前以知的Section 大于已经存储的Section

  9. // Periodically print an upgrade log message to the user

  10. // 每隔8秒打印一次日志信息。

  11. if time.Since(updated) > 8*time.Second {

  12. if c.knownSections > c.storedSections+1 {

  13. updating = true

  14. c.log.Info("Upgrading chain index", "percentage", c.storedSections*100/c.knownSections)

  15. }

  16. updated = time.Now()

  17. }

  18. // Cache the current section count and head to allow unlocking the mutex

  19. section := c.storedSections

  20. var oldHead common.Hash

  21. if section > 0 { // section - 1 代表section的下标是从0开始的。

  22. // sectionHead用来获取section的最后一个区块的hash值。

  23. oldHead = c.sectionHead(section - 1)

  24. }

  25. // Process the newly defined section in the background

  26. c.lock.Unlock()

  27. // 处理 返回新的section的最后一个区块的hash值

  28. newHead, err := c.processSection(section, oldHead)

  29. if err != nil {

  30. c.log.Error("Section processing failed", "error", err)

  31. }

  32. c.lock.Lock()

  33.  

  34. // If processing succeeded and no reorgs occcurred, mark the section completed

  35. if err == nil && oldHead == c.sectionHead(section-1) {

  36. c.setSectionHead(section, newHead) // 更新数据库的状态

  37. c.setValidSections(section + 1) // 更新数据库状态

  38. if c.storedSections == c.knownSections && updating {

  39. updating = false

  40. c.log.Info("Finished upgrading chain index")

  41. }

  42. // cascadedHead 是更新后的section的最后一个区块的高度

  43. // 用法是什么 ?

  44. c.cascadedHead = c.storedSections*c.sectionSize - 1

  45. for _, child := range c.children {

  46. c.log.Trace("Cascading chain index update", "head", c.cascadedHead)

  47. child.newHead(c.cascadedHead, false)

  48. }

  49. } else { //如果处理失败,那么在有新的通知之前不会重试。

  50. // If processing failed, don't retry until further notification

  51. c.log.Debug("Chain index processing failed", "section", section, "err", err)

  52. c.knownSections = c.storedSections

  53. }

  54. }

  55. // If there are still further sections to process, reschedule

  56. // 如果还有section等待处理,那么等待throttling时间再处理。避免磁盘过载。

  57. if c.knownSections > c.storedSections {

  58. time.AfterFunc(c.throttling, func() {

  59. select {

  60. case c.update <- struct{}{}:

  61. default:

  62. }

  63. })

  64. }

  65. c.lock.Unlock()

  66. }

  67. }

  68. }

未完待续…


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/31557831/viewspace-2216868/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2018-10-11

  • 博文量
    79
  • 访问量
    59488