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

266 lines
7.3 KiB

3 years ago
4 years ago
4 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
5 years ago
5 years ago
  1. package filer
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "math"
  7. "sort"
  8. "strings"
  9. "time"
  10. "github.com/chrislusf/seaweedfs/weed/glog"
  11. "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
  12. "github.com/chrislusf/seaweedfs/weed/stats"
  13. "github.com/chrislusf/seaweedfs/weed/util"
  14. "github.com/chrislusf/seaweedfs/weed/wdclient"
  15. )
  16. func StreamContent(masterClient wdclient.HasLookupFileIdFunction, w io.Writer, chunks []*filer_pb.FileChunk, offset int64, size int64) error {
  17. glog.V(9).Infof("start to stream content for chunks: %+v\n", chunks)
  18. chunkViews := ViewFromChunks(masterClient.GetLookupFileIdFunction(), chunks, offset, size)
  19. fileId2Url := make(map[string][]string)
  20. for _, chunkView := range chunkViews {
  21. urlStrings, err := masterClient.GetLookupFileIdFunction()(chunkView.FileId)
  22. if err != nil {
  23. glog.V(1).Infof("operation LookupFileId %s failed, err: %v", chunkView.FileId, err)
  24. return err
  25. } else if len(urlStrings) == 0 {
  26. glog.Errorf("operation LookupFileId %s failed, err: urls not found", chunkView.FileId)
  27. return fmt.Errorf("operation LookupFileId %s failed, err: urls not found", chunkView.FileId)
  28. }
  29. fileId2Url[chunkView.FileId] = urlStrings
  30. }
  31. for _, chunkView := range chunkViews {
  32. urlStrings := fileId2Url[chunkView.FileId]
  33. start := time.Now()
  34. data, err := retriedFetchChunkData(urlStrings, chunkView.CipherKey, chunkView.IsGzipped, chunkView.IsFullChunk(), chunkView.Offset, int(chunkView.Size))
  35. stats.FilerRequestHistogram.WithLabelValues("chunkDownload").Observe(time.Since(start).Seconds())
  36. if err != nil {
  37. stats.FilerRequestCounter.WithLabelValues("chunkDownloadError").Inc()
  38. return fmt.Errorf("read chunk: %v", err)
  39. }
  40. _, err = w.Write(data)
  41. if err != nil {
  42. stats.FilerRequestCounter.WithLabelValues("chunkDownloadedError").Inc()
  43. return fmt.Errorf("write chunk: %v", err)
  44. }
  45. stats.FilerRequestCounter.WithLabelValues("chunkDownload").Inc()
  46. }
  47. return nil
  48. }
  49. // ---------------- ReadAllReader ----------------------------------
  50. func ReadAll(masterClient *wdclient.MasterClient, chunks []*filer_pb.FileChunk) ([]byte, error) {
  51. buffer := bytes.Buffer{}
  52. lookupFileIdFn := func(fileId string) (targetUrls []string, err error) {
  53. return masterClient.LookupFileId(fileId)
  54. }
  55. chunkViews := ViewFromChunks(lookupFileIdFn, chunks, 0, math.MaxInt64)
  56. for _, chunkView := range chunkViews {
  57. urlStrings, err := lookupFileIdFn(chunkView.FileId)
  58. if err != nil {
  59. glog.V(1).Infof("operation LookupFileId %s failed, err: %v", chunkView.FileId, err)
  60. return nil, err
  61. }
  62. data, err := retriedFetchChunkData(urlStrings, chunkView.CipherKey, chunkView.IsGzipped, chunkView.IsFullChunk(), chunkView.Offset, int(chunkView.Size))
  63. if err != nil {
  64. return nil, err
  65. }
  66. buffer.Write(data)
  67. }
  68. return buffer.Bytes(), nil
  69. }
  70. // ---------------- ChunkStreamReader ----------------------------------
  71. type ChunkStreamReader struct {
  72. chunkViews []*ChunkView
  73. totalSize int64
  74. logicOffset int64
  75. buffer []byte
  76. bufferOffset int64
  77. bufferPos int
  78. nextChunkViewIndex int
  79. lookupFileId wdclient.LookupFileIdFunctionType
  80. }
  81. var _ = io.ReadSeeker(&ChunkStreamReader{})
  82. var _ = io.ReaderAt(&ChunkStreamReader{})
  83. func doNewChunkStreamReader(lookupFileIdFn wdclient.LookupFileIdFunctionType, chunks []*filer_pb.FileChunk) *ChunkStreamReader {
  84. chunkViews := ViewFromChunks(lookupFileIdFn, chunks, 0, math.MaxInt64)
  85. sort.Slice(chunkViews, func(i, j int) bool {
  86. return chunkViews[i].LogicOffset < chunkViews[j].LogicOffset
  87. })
  88. var totalSize int64
  89. for _, chunk := range chunkViews {
  90. totalSize += int64(chunk.Size)
  91. }
  92. return &ChunkStreamReader{
  93. chunkViews: chunkViews,
  94. lookupFileId: lookupFileIdFn,
  95. totalSize: totalSize,
  96. }
  97. }
  98. func NewChunkStreamReaderFromFiler(masterClient *wdclient.MasterClient, chunks []*filer_pb.FileChunk) *ChunkStreamReader {
  99. lookupFileIdFn := func(fileId string) (targetUrl []string, err error) {
  100. return masterClient.LookupFileId(fileId)
  101. }
  102. return doNewChunkStreamReader(lookupFileIdFn, chunks)
  103. }
  104. func NewChunkStreamReader(filerClient filer_pb.FilerClient, chunks []*filer_pb.FileChunk) *ChunkStreamReader {
  105. lookupFileIdFn := LookupFn(filerClient)
  106. return doNewChunkStreamReader(lookupFileIdFn, chunks)
  107. }
  108. func (c *ChunkStreamReader) ReadAt(p []byte, off int64) (n int, err error) {
  109. if err = c.prepareBufferFor(c.logicOffset); err != nil {
  110. return
  111. }
  112. return c.Read(p)
  113. }
  114. func (c *ChunkStreamReader) Read(p []byte) (n int, err error) {
  115. for n < len(p) {
  116. if c.isBufferEmpty() {
  117. if c.nextChunkViewIndex >= len(c.chunkViews) {
  118. return n, io.EOF
  119. }
  120. chunkView := c.chunkViews[c.nextChunkViewIndex]
  121. if err = c.fetchChunkToBuffer(chunkView); err != nil {
  122. return
  123. }
  124. c.nextChunkViewIndex++
  125. }
  126. t := copy(p[n:], c.buffer[c.bufferPos:])
  127. c.bufferPos += t
  128. n += t
  129. c.logicOffset += int64(t)
  130. }
  131. return
  132. }
  133. func (c *ChunkStreamReader) isBufferEmpty() bool {
  134. return len(c.buffer) <= c.bufferPos
  135. }
  136. func (c *ChunkStreamReader) Seek(offset int64, whence int) (int64, error) {
  137. var err error
  138. switch whence {
  139. case io.SeekStart:
  140. case io.SeekCurrent:
  141. offset += c.logicOffset
  142. case io.SeekEnd:
  143. offset = c.totalSize + offset
  144. }
  145. if offset > c.totalSize {
  146. err = io.ErrUnexpectedEOF
  147. } else {
  148. c.logicOffset = offset
  149. }
  150. return offset, err
  151. }
  152. func (c *ChunkStreamReader) prepareBufferFor(offset int64) (err error) {
  153. // stay in the same chunk
  154. if !c.isBufferEmpty() {
  155. if c.bufferOffset <= offset && offset < c.bufferOffset+int64(len(c.buffer)) {
  156. c.bufferPos = int(offset - c.bufferOffset)
  157. return nil
  158. }
  159. }
  160. // need to seek to a different chunk
  161. currentChunkIndex := sort.Search(len(c.chunkViews), func(i int) bool {
  162. return c.chunkViews[i].LogicOffset <= offset
  163. })
  164. if currentChunkIndex == len(c.chunkViews) {
  165. return io.EOF
  166. }
  167. // positioning within the new chunk
  168. chunk := c.chunkViews[currentChunkIndex]
  169. if chunk.LogicOffset <= offset && offset < chunk.LogicOffset+int64(chunk.Size) {
  170. if c.isBufferEmpty() || c.bufferOffset != chunk.LogicOffset {
  171. if err = c.fetchChunkToBuffer(chunk); err != nil {
  172. return
  173. }
  174. c.nextChunkViewIndex = currentChunkIndex + 1
  175. }
  176. c.bufferPos = int(offset - c.bufferOffset)
  177. }
  178. return
  179. }
  180. func (c *ChunkStreamReader) fetchChunkToBuffer(chunkView *ChunkView) error {
  181. urlStrings, err := c.lookupFileId(chunkView.FileId)
  182. if err != nil {
  183. glog.V(1).Infof("operation LookupFileId %s failed, err: %v", chunkView.FileId, err)
  184. return err
  185. }
  186. var buffer bytes.Buffer
  187. var shouldRetry bool
  188. for _, urlString := range urlStrings {
  189. shouldRetry, err = util.ReadUrlAsStream(urlString, chunkView.CipherKey, chunkView.IsGzipped, chunkView.IsFullChunk(), chunkView.Offset, int(chunkView.Size), func(data []byte) {
  190. buffer.Write(data)
  191. })
  192. if !shouldRetry {
  193. break
  194. }
  195. if err != nil {
  196. glog.V(1).Infof("read %s failed, err: %v", chunkView.FileId, err)
  197. buffer.Reset()
  198. } else {
  199. break
  200. }
  201. }
  202. if err != nil {
  203. return err
  204. }
  205. c.buffer = buffer.Bytes()
  206. c.bufferPos = 0
  207. c.bufferOffset = chunkView.LogicOffset
  208. // glog.V(0).Infof("read %s [%d,%d)", chunkView.FileId, chunkView.LogicOffset, chunkView.LogicOffset+int64(chunkView.Size))
  209. return nil
  210. }
  211. func (c *ChunkStreamReader) Close() {
  212. // TODO try to release and reuse buffer
  213. }
  214. func VolumeId(fileId string) string {
  215. lastCommaIndex := strings.LastIndex(fileId, ",")
  216. if lastCommaIndex > 0 {
  217. return fileId[:lastCommaIndex]
  218. }
  219. return fileId
  220. }