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 5.8 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  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. downloadingStream io.ReadCloser
  33. downloadingStripIndex int64
  34. downloadingPlanCtxCancel func()
  35. }
  36. type dataChanEntry struct {
  37. Data []byte
  38. Position int64 // 条带在文件中的位置。字节为单位
  39. Error error
  40. }
  41. func NewStripIterator(object cdssdk.Object, blocks []downloadBlock, red *cdssdk.ECRedundancy, beginStripIndex int64, cache *StripCache, maxPrefetch int) *StripIterator {
  42. if maxPrefetch <= 0 {
  43. maxPrefetch = 1
  44. }
  45. iter := &StripIterator{
  46. object: object,
  47. blocks: blocks,
  48. red: red,
  49. curStripIndex: beginStripIndex,
  50. cache: cache,
  51. dataChan: make(chan dataChanEntry, maxPrefetch-1),
  52. downloadingDone: make(chan any),
  53. }
  54. return iter
  55. }
  56. func (s *StripIterator) MoveNext() (Strip, error) {
  57. if !s.inited {
  58. go s.downloading(s.curStripIndex)
  59. s.inited = true
  60. }
  61. // 先尝试获取一下,用于判断本次获取是否发生了等待
  62. select {
  63. case entry, ok := <-s.dataChan:
  64. if !ok || entry.Error == io.EOF {
  65. return Strip{}, iterator.ErrNoMoreItem
  66. }
  67. if entry.Error != nil {
  68. return Strip{}, entry.Error
  69. }
  70. s.curStripIndex++
  71. return Strip{Data: entry.Data, Position: entry.Position}, nil
  72. default:
  73. logger.Debugf("waitting for ec strip %v of object %v", s.curStripIndex, s.object.ObjectID)
  74. }
  75. // 发生了等待
  76. select {
  77. case entry, ok := <-s.dataChan:
  78. if !ok || entry.Error == io.EOF {
  79. return Strip{}, iterator.ErrNoMoreItem
  80. }
  81. if entry.Error != nil {
  82. return Strip{}, entry.Error
  83. }
  84. s.curStripIndex++
  85. return Strip{Data: entry.Data, Position: entry.Position}, nil
  86. case <-s.downloadingDone:
  87. return Strip{}, iterator.ErrNoMoreItem
  88. }
  89. }
  90. func (s *StripIterator) Close() {
  91. s.downloadingDoneOnce.Do(func() {
  92. close(s.downloadingDone)
  93. })
  94. }
  95. func (s *StripIterator) downloading(startStripIndex int64) {
  96. curStripIndex := startStripIndex
  97. loop:
  98. for {
  99. stripBytesPos := curStripIndex * int64(s.red.K) * int64(s.red.ChunkSize)
  100. if stripBytesPos >= s.object.Size {
  101. s.sendToDataChan(dataChanEntry{Error: io.EOF})
  102. break
  103. }
  104. stripKey := ECStripKey{
  105. ObjectID: s.object.ObjectID,
  106. StripIndex: curStripIndex,
  107. }
  108. item, ok := s.cache.Get(stripKey)
  109. if ok {
  110. if item.ObjectFileHash == s.object.FileHash {
  111. if !s.sendToDataChan(dataChanEntry{Data: item.Data, Position: stripBytesPos}) {
  112. break loop
  113. }
  114. curStripIndex++
  115. continue
  116. } else {
  117. // 如果Object的Hash和Cache的Hash不一致,说明Cache是无效的,需要重新下载
  118. s.cache.Remove(stripKey)
  119. }
  120. }
  121. dataBuf := make([]byte, int64(s.red.K*s.red.ChunkSize))
  122. n, err := s.readStrip(curStripIndex, dataBuf)
  123. if err == io.ErrUnexpectedEOF {
  124. // dataBuf中的内容可能不足一个条带,但仍然将其完整放入cache中,外部应该自行计算该从这个buffer中读多少数据
  125. s.cache.Add(stripKey, ObjectECStrip{
  126. Data: dataBuf,
  127. ObjectFileHash: s.object.FileHash,
  128. })
  129. s.sendToDataChan(dataChanEntry{Data: dataBuf[:n], Position: stripBytesPos})
  130. s.sendToDataChan(dataChanEntry{Error: io.EOF})
  131. break loop
  132. }
  133. if err != nil {
  134. s.sendToDataChan(dataChanEntry{Error: err})
  135. break loop
  136. }
  137. s.cache.Add(stripKey, ObjectECStrip{
  138. Data: dataBuf,
  139. ObjectFileHash: s.object.FileHash,
  140. })
  141. if !s.sendToDataChan(dataChanEntry{Data: dataBuf, Position: stripBytesPos}) {
  142. break loop
  143. }
  144. curStripIndex++
  145. }
  146. close(s.dataChan)
  147. }
  148. func (s *StripIterator) sendToDataChan(entry dataChanEntry) bool {
  149. select {
  150. case s.dataChan <- entry:
  151. return true
  152. case <-s.downloadingDone:
  153. return false
  154. }
  155. }
  156. func (s *StripIterator) readStrip(stripIndex int64, buf []byte) (int, error) {
  157. // 如果需求的条带不当前正在下载的条带的位置不符合,则需要重新打开下载流
  158. if s.downloadingStream == nil || s.downloadingStripIndex != stripIndex {
  159. if s.downloadingStream != nil {
  160. s.downloadingStream.Close()
  161. s.downloadingPlanCtxCancel()
  162. }
  163. ft := ioswitch2.NewFromTo()
  164. for _, b := range s.blocks {
  165. node := b.Node
  166. ft.AddFrom(ioswitch2.NewFromNode(b.Block.FileHash, &node, b.Block.Index))
  167. }
  168. toExec, hd := ioswitch2.NewToDriverWithRange(-1, exec.Range{
  169. Offset: stripIndex * s.red.StripSize(),
  170. })
  171. ft.AddTo(toExec)
  172. parser := parser.NewParser(*s.red)
  173. plans := exec.NewPlanBuilder()
  174. err := parser.Parse(ft, plans)
  175. if err != nil {
  176. return 0, err
  177. }
  178. // TODo2 注入依赖
  179. exeCtx := exec.NewExecContext()
  180. exec := plans.Execute(exeCtx)
  181. ctx, cancel := context.WithCancel(context.Background())
  182. go exec.Wait(ctx)
  183. str, err := exec.BeginRead(hd)
  184. if err != nil {
  185. cancel()
  186. return 0, err
  187. }
  188. s.downloadingStream = str
  189. s.downloadingStripIndex = stripIndex
  190. s.downloadingPlanCtxCancel = cancel
  191. }
  192. n, err := io.ReadFull(s.downloadingStream, buf)
  193. s.downloadingStripIndex += 1
  194. return n, err
  195. }

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