You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

strip_iterator.go 4.9 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. package downloader
  2. import (
  3. "context"
  4. "io"
  5. "sync"
  6. "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec"
  7. "gitlink.org.cn/cloudream/common/pkgs/iterator"
  8. "gitlink.org.cn/cloudream/common/pkgs/logger"
  9. cdssdk "gitlink.org.cn/cloudream/common/sdks/storage"
  10. stgmod "gitlink.org.cn/cloudream/storage/common/models"
  11. "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2"
  12. "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2/parser"
  13. )
  14. type downloadBlock struct {
  15. Node cdssdk.Node
  16. Block stgmod.ObjectBlock
  17. }
  18. type Strip struct {
  19. Data []byte
  20. Position int64
  21. }
  22. type StripIterator struct {
  23. object cdssdk.Object
  24. blocks []downloadBlock
  25. red *cdssdk.ECRedundancy
  26. curStripIndex int64
  27. cache *StripCache
  28. dataChan chan dataChanEntry
  29. downloadingDone chan any
  30. downloadingDoneOnce sync.Once
  31. inited bool
  32. }
  33. type dataChanEntry struct {
  34. Data []byte
  35. Position int64 // 条带在文件中的位置。字节为单位
  36. Error error
  37. }
  38. func NewStripIterator(object cdssdk.Object, blocks []downloadBlock, red *cdssdk.ECRedundancy, beginStripIndex int64, cache *StripCache, maxPrefetch int) *StripIterator {
  39. if maxPrefetch <= 0 {
  40. maxPrefetch = 1
  41. }
  42. iter := &StripIterator{
  43. object: object,
  44. blocks: blocks,
  45. red: red,
  46. curStripIndex: beginStripIndex,
  47. cache: cache,
  48. dataChan: make(chan dataChanEntry, maxPrefetch-1),
  49. downloadingDone: make(chan any),
  50. }
  51. return iter
  52. }
  53. func (s *StripIterator) MoveNext() (Strip, error) {
  54. if !s.inited {
  55. go s.downloading()
  56. s.inited = true
  57. }
  58. // 先尝试获取一下,用于判断本次获取是否发生了等待
  59. select {
  60. case entry, ok := <-s.dataChan:
  61. if !ok || entry.Error == io.EOF {
  62. return Strip{}, iterator.ErrNoMoreItem
  63. }
  64. if entry.Error != nil {
  65. return Strip{}, entry.Error
  66. }
  67. s.curStripIndex++
  68. return Strip{Data: entry.Data, Position: entry.Position}, nil
  69. default:
  70. logger.Debugf("waitting for ec strip %v of object %v", s.curStripIndex, s.object.ObjectID)
  71. }
  72. // 发生了等待
  73. select {
  74. case entry, ok := <-s.dataChan:
  75. if !ok || entry.Error == io.EOF {
  76. return Strip{}, iterator.ErrNoMoreItem
  77. }
  78. if entry.Error != nil {
  79. return Strip{}, entry.Error
  80. }
  81. s.curStripIndex++
  82. return Strip{Data: entry.Data, Position: entry.Position}, nil
  83. case <-s.downloadingDone:
  84. return Strip{}, iterator.ErrNoMoreItem
  85. }
  86. }
  87. func (s *StripIterator) Close() {
  88. s.downloadingDoneOnce.Do(func() {
  89. close(s.downloadingDone)
  90. })
  91. }
  92. func (s *StripIterator) downloading() {
  93. ft := ioswitch2.NewFromTo()
  94. for _, b := range s.blocks {
  95. ft.AddFrom(ioswitch2.NewFromNode(b.Block.FileHash, &b.Node, b.Block.Index))
  96. }
  97. toExec, hd := ioswitch2.NewToDriverWithRange(-1, exec.Range{
  98. Offset: s.curStripIndex * int64(s.red.ChunkSize*s.red.K),
  99. })
  100. ft.AddTo(toExec)
  101. parser := parser.NewParser(*s.red)
  102. plans := exec.NewPlanBuilder()
  103. err := parser.Parse(ft, plans)
  104. if err != nil {
  105. s.sendToDataChan(dataChanEntry{Error: err})
  106. return
  107. }
  108. exec := plans.Execute()
  109. ctx, cancel := context.WithCancel(context.Background())
  110. go exec.Wait(ctx)
  111. defer cancel()
  112. str, err := exec.BeginRead(hd)
  113. if err != nil {
  114. s.sendToDataChan(dataChanEntry{Error: err})
  115. return
  116. }
  117. curStripIndex := s.curStripIndex
  118. loop:
  119. for {
  120. stripBytesPos := curStripIndex * int64(s.red.K) * int64(s.red.ChunkSize)
  121. if stripBytesPos >= s.object.Size {
  122. s.sendToDataChan(dataChanEntry{Error: io.EOF})
  123. break
  124. }
  125. stripKey := ECStripKey{
  126. ObjectID: s.object.ObjectID,
  127. StripIndex: curStripIndex,
  128. }
  129. item, ok := s.cache.Get(stripKey)
  130. if ok {
  131. if item.ObjectFileHash == s.object.FileHash {
  132. if !s.sendToDataChan(dataChanEntry{Data: item.Data, Position: stripBytesPos}) {
  133. break loop
  134. }
  135. curStripIndex++
  136. continue
  137. } else {
  138. // 如果Object的Hash和Cache的Hash不一致,说明Cache是无效的,需要重新下载
  139. s.cache.Remove(stripKey)
  140. }
  141. }
  142. dataBuf := make([]byte, int64(s.red.K*s.red.ChunkSize))
  143. n, err := io.ReadFull(str, dataBuf)
  144. if err == io.ErrUnexpectedEOF {
  145. s.cache.Add(stripKey, ObjectECStrip{
  146. Data: dataBuf,
  147. ObjectFileHash: s.object.FileHash,
  148. })
  149. s.sendToDataChan(dataChanEntry{Data: dataBuf[:n], Position: stripBytesPos})
  150. s.sendToDataChan(dataChanEntry{Error: io.EOF})
  151. break loop
  152. }
  153. if err != nil {
  154. s.sendToDataChan(dataChanEntry{Error: err})
  155. break loop
  156. }
  157. s.cache.Add(stripKey, ObjectECStrip{
  158. Data: dataBuf,
  159. ObjectFileHash: s.object.FileHash,
  160. })
  161. if !s.sendToDataChan(dataChanEntry{Data: dataBuf, Position: stripBytesPos}) {
  162. break loop
  163. }
  164. curStripIndex++
  165. }
  166. close(s.dataChan)
  167. }
  168. func (s *StripIterator) sendToDataChan(entry dataChanEntry) bool {
  169. select {
  170. case s.dataChan <- entry:
  171. return true
  172. case <-s.downloadingDone:
  173. return false
  174. }
  175. }

本项目旨在将云际存储公共基础设施化,使个人及企业可低门槛使用高效的云际存储服务(安装开箱即用云际存储客户端即可,无需关注其他组件的部署),同时支持用户灵活便捷定制云际存储的功能细节。