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

7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. package downloader
  2. import (
  3. "context"
  4. "io"
  5. "sync"
  6. "gitlink.org.cn/cloudream/common/pkgs/iterator"
  7. "gitlink.org.cn/cloudream/common/pkgs/logger"
  8. "gitlink.org.cn/cloudream/common/utils/math2"
  9. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch/exec"
  10. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2"
  11. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2/ops2"
  12. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2/parser"
  13. "gitlink.org.cn/cloudream/jcs-pub/common/types"
  14. )
  15. type downloadBlock struct {
  16. Space types.UserSpaceDetail
  17. Block types.ObjectBlock
  18. }
  19. type Strip struct {
  20. Data []byte
  21. Position int64
  22. }
  23. type StripIterator struct {
  24. downloader *Downloader
  25. object types.Object
  26. blocks []downloadBlock
  27. red types.ECRedundancy
  28. curStripIndex int64
  29. cache *StripCache
  30. dataChan chan dataChanEntry
  31. downloadingDone chan any
  32. downloadingDoneOnce sync.Once
  33. inited bool
  34. downloadingStream io.ReadCloser
  35. downloadingStripIndex int64
  36. downloadingPlanCtxCancel func()
  37. }
  38. type dataChanEntry struct {
  39. Data []byte
  40. Position int64 // 条带在文件中的位置。字节为单位
  41. Error error
  42. }
  43. func NewStripIterator(downloader *Downloader, object types.Object, blocks []downloadBlock, red types.ECRedundancy, beginStripIndex int64, cache *StripCache, maxPrefetch int) *StripIterator {
  44. if maxPrefetch <= 0 {
  45. maxPrefetch = 1
  46. }
  47. iter := &StripIterator{
  48. downloader: downloader,
  49. object: object,
  50. blocks: blocks,
  51. red: red,
  52. curStripIndex: beginStripIndex,
  53. cache: cache,
  54. dataChan: make(chan dataChanEntry, maxPrefetch-1),
  55. downloadingDone: make(chan any),
  56. }
  57. return iter
  58. }
  59. func (s *StripIterator) MoveNext() (Strip, error) {
  60. if !s.inited {
  61. go s.downloading(s.curStripIndex)
  62. s.inited = true
  63. }
  64. // 先尝试获取一下,用于判断本次获取是否发生了等待
  65. select {
  66. case entry, ok := <-s.dataChan:
  67. if !ok || entry.Error == io.EOF {
  68. return Strip{}, iterator.ErrNoMoreItem
  69. }
  70. if entry.Error != nil {
  71. return Strip{}, entry.Error
  72. }
  73. s.curStripIndex++
  74. return Strip{Data: entry.Data, Position: entry.Position}, nil
  75. default:
  76. logger.Debugf("waitting for ec strip %v of object %v", s.curStripIndex, s.object.ObjectID)
  77. }
  78. // 发生了等待
  79. select {
  80. case entry, ok := <-s.dataChan:
  81. if !ok || entry.Error == io.EOF {
  82. return Strip{}, iterator.ErrNoMoreItem
  83. }
  84. if entry.Error != nil {
  85. return Strip{}, entry.Error
  86. }
  87. s.curStripIndex++
  88. return Strip{Data: entry.Data, Position: entry.Position}, nil
  89. case <-s.downloadingDone:
  90. return Strip{}, iterator.ErrNoMoreItem
  91. }
  92. }
  93. func (s *StripIterator) Close() {
  94. s.downloadingDoneOnce.Do(func() {
  95. close(s.downloadingDone)
  96. })
  97. }
  98. func (s *StripIterator) downloading(startStripIndex int64) {
  99. curStripIndex := startStripIndex
  100. loop:
  101. for {
  102. stripBytesPos := curStripIndex * int64(s.red.K) * int64(s.red.ChunkSize)
  103. if stripBytesPos >= s.object.Size {
  104. s.sendToDataChan(dataChanEntry{Error: io.EOF})
  105. break
  106. }
  107. stripKey := ECStripKey{
  108. ObjectID: s.object.ObjectID,
  109. StripIndex: curStripIndex,
  110. }
  111. item, ok := s.cache.Get(stripKey)
  112. if ok {
  113. if item.ObjectFileHash == s.object.FileHash {
  114. if !s.sendToDataChan(dataChanEntry{Data: item.Data, Position: stripBytesPos}) {
  115. break loop
  116. }
  117. curStripIndex++
  118. continue
  119. } else {
  120. // 如果Object的Hash和Cache的Hash不一致,说明Cache是无效的,需要重新下载
  121. s.cache.Remove(stripKey)
  122. }
  123. }
  124. dataBuf := make([]byte, int64(s.red.K*s.red.ChunkSize))
  125. n, err := s.readStrip(curStripIndex, dataBuf)
  126. if err == io.ErrUnexpectedEOF {
  127. // dataBuf中的内容可能不足一个条带,但仍然将其完整放入cache中,外部应该自行计算该从这个buffer中读多少数据
  128. s.cache.Add(stripKey, ObjectECStrip{
  129. Data: dataBuf,
  130. ObjectFileHash: s.object.FileHash,
  131. })
  132. s.sendToDataChan(dataChanEntry{Data: dataBuf[:n], Position: stripBytesPos})
  133. s.sendToDataChan(dataChanEntry{Error: io.EOF})
  134. break loop
  135. }
  136. if err != nil {
  137. s.sendToDataChan(dataChanEntry{Error: err})
  138. break loop
  139. }
  140. s.cache.Add(stripKey, ObjectECStrip{
  141. Data: dataBuf,
  142. ObjectFileHash: s.object.FileHash,
  143. })
  144. if !s.sendToDataChan(dataChanEntry{Data: dataBuf, Position: stripBytesPos}) {
  145. break loop
  146. }
  147. curStripIndex++
  148. }
  149. if s.downloadingStream != nil {
  150. s.downloadingStream.Close()
  151. s.downloadingPlanCtxCancel()
  152. }
  153. close(s.dataChan)
  154. }
  155. func (s *StripIterator) sendToDataChan(entry dataChanEntry) bool {
  156. select {
  157. case s.dataChan <- entry:
  158. return true
  159. case <-s.downloadingDone:
  160. return false
  161. }
  162. }
  163. func (s *StripIterator) readStrip(stripIndex int64, buf []byte) (int, error) {
  164. // 如果需求的条带不当前正在下载的条带的位置不符合,则需要重新打开下载流
  165. if s.downloadingStream == nil || s.downloadingStripIndex != stripIndex {
  166. if s.downloadingStream != nil {
  167. s.downloadingStream.Close()
  168. s.downloadingPlanCtxCancel()
  169. }
  170. ft := ioswitch2.NewFromTo()
  171. ft.ECParam = &s.red
  172. for _, b := range s.blocks {
  173. space := b.Space
  174. ft.AddFrom(ioswitch2.NewFromShardstore(b.Block.FileHash, space, ioswitch2.ECStream(b.Block.Index)))
  175. }
  176. toExec, hd := ioswitch2.NewToDriverWithRange(ioswitch2.RawStream(), math2.Range{
  177. Offset: stripIndex * s.red.StripSize(),
  178. })
  179. ft.AddTo(toExec)
  180. plans := exec.NewPlanBuilder()
  181. err := parser.Parse(ft, plans)
  182. if err != nil {
  183. return 0, err
  184. }
  185. exeCtx := exec.NewExecContext()
  186. exec.SetValueByType(exeCtx, s.downloader.stgPool)
  187. exec := plans.Execute(exeCtx)
  188. ctx, cancel := context.WithCancel(context.Background())
  189. go func() {
  190. ret, err := exec.Wait(ctx)
  191. if err != nil {
  192. logger.Warnf("downloading strip: %v", err)
  193. return
  194. }
  195. for _, v := range ret.GetArray(ops2.BaseReadStatsStoreKey) {
  196. v2 := v.(*ops2.BaseReadStatsValue)
  197. s.downloader.speedStats.Record(v2.Size, v2.Time, v2.Location.IsDriver)
  198. }
  199. }()
  200. str, err := exec.BeginRead(hd)
  201. if err != nil {
  202. cancel()
  203. return 0, err
  204. }
  205. s.downloadingStream = str
  206. s.downloadingStripIndex = stripIndex
  207. s.downloadingPlanCtxCancel = cancel
  208. }
  209. n, err := io.ReadFull(s.downloadingStream, buf)
  210. s.downloadingStripIndex += 1
  211. return n, err
  212. }

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