|
- package iterator
-
- import (
- "fmt"
- "io"
- "math/rand"
-
- "github.com/samber/lo"
-
- cdssdk "gitlink.org.cn/cloudream/common/sdks/storage"
-
- myio "gitlink.org.cn/cloudream/common/utils/io"
- stgglb "gitlink.org.cn/cloudream/storage/common/globals"
- stgmodels "gitlink.org.cn/cloudream/storage/common/models"
- "gitlink.org.cn/cloudream/storage/common/pkgs/db/model"
- "gitlink.org.cn/cloudream/storage/common/pkgs/ec"
- coormq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/coordinator"
- )
-
- type ECObjectIterator struct {
- OnClosing func()
-
- objects []model.Object
- objectECData []stgmodels.ObjectECData
- currentIndex int
- inited bool
-
- ecInfo cdssdk.ECRedundancyInfo
- ec model.Ec
- downloadCtx *DownloadContext
- cliLocation model.Location
- }
-
- func NewECObjectIterator(objects []model.Object, objectECData []stgmodels.ObjectECData, ecInfo cdssdk.ECRedundancyInfo, ec model.Ec, downloadCtx *DownloadContext) *ECObjectIterator {
- return &ECObjectIterator{
- objects: objects,
- objectECData: objectECData,
- ecInfo: ecInfo,
- ec: ec,
- downloadCtx: downloadCtx,
- }
- }
-
- func (i *ECObjectIterator) MoveNext() (*IterDownloadingObject, error) {
- // TODO 加锁
- coorCli, err := stgglb.CoordinatorMQPool.Acquire()
- if err != nil {
- return nil, fmt.Errorf("new coordinator client: %w", err)
- }
- defer stgglb.CoordinatorMQPool.Release(coorCli)
-
- if !i.inited {
- i.inited = true
-
- findCliLocResp, err := coorCli.FindClientLocation(coormq.NewFindClientLocation(stgglb.Local.ExternalIP))
- if err != nil {
- return nil, fmt.Errorf("finding client location: %w", err)
- }
- i.cliLocation = findCliLocResp.Location
- }
-
- if i.currentIndex >= len(i.objects) {
- return nil, ErrNoMoreItem
- }
-
- item, err := i.doMove(coorCli)
- i.currentIndex++
- return item, err
- }
-
- func (iter *ECObjectIterator) doMove(coorCli *coormq.Client) (*IterDownloadingObject, error) {
- obj := iter.objects[iter.currentIndex]
- ecData := iter.objectECData[iter.currentIndex]
-
- //采取直接读,优先选内网节点
- var chosenNodes []DownloadNodeInfo
- var chosenBlocks []stgmodels.ObjectBlockData
- for i := range ecData.Blocks {
- if len(chosenBlocks) == iter.ec.EcK {
- break
- }
-
- // 块没有被任何节点缓存或者获取失败都没关系,只要能获取到k个块的信息就行
-
- if len(ecData.Blocks[i].NodeIDs) == 0 {
- continue
- }
-
- getNodesResp, err := coorCli.GetNodes(coormq.NewGetNodes(ecData.Blocks[i].NodeIDs))
- if err != nil {
- continue
- }
-
- downloadNodes := lo.Map(getNodesResp.Nodes, func(node model.Node, index int) DownloadNodeInfo {
- return DownloadNodeInfo{
- Node: node,
- IsSameLocation: node.LocationID == iter.cliLocation.LocationID,
- }
- })
-
- chosenBlocks = append(chosenBlocks, ecData.Blocks[i])
- chosenNodes = append(chosenNodes, iter.chooseDownloadNode(downloadNodes))
-
- }
-
- if len(chosenBlocks) < iter.ec.EcK {
- return nil, fmt.Errorf("no enough blocks to reconstruct the file, want %d, get only %d", iter.ec.EcK, len(chosenBlocks))
- }
-
- reader, err := iter.downloadEcObject(iter.downloadCtx, obj.Size, chosenNodes, chosenBlocks)
- if err != nil {
- return nil, fmt.Errorf("ec read failed, err: %w", err)
- }
-
- return &IterDownloadingObject{
- Object: obj,
- File: reader,
- }, nil
- }
-
- func (i *ECObjectIterator) Close() {
- if i.OnClosing != nil {
- i.OnClosing()
- }
- }
-
- // chooseDownloadNode 选择一个下载节点
- // 1. 从与当前客户端相同地域的节点中随机选一个
- // 2. 没有用的话从所有节点中随机选一个
- func (i *ECObjectIterator) chooseDownloadNode(entries []DownloadNodeInfo) DownloadNodeInfo {
- sameLocationEntries := lo.Filter(entries, func(e DownloadNodeInfo, i int) bool { return e.IsSameLocation })
- if len(sameLocationEntries) > 0 {
- return sameLocationEntries[rand.Intn(len(sameLocationEntries))]
- }
-
- return entries[rand.Intn(len(entries))]
- }
-
- func (iter *ECObjectIterator) downloadEcObject(ctx *DownloadContext, fileSize int64, nodes []DownloadNodeInfo, blocks []stgmodels.ObjectBlockData) (io.ReadCloser, error) {
- var fileStrs []io.ReadCloser
-
- rs, err := ec.NewRs(iter.ec.EcK, iter.ec.EcN, iter.ecInfo.ChunkSize)
- if err != nil {
- return nil, fmt.Errorf("new rs: %w", err)
- }
-
- for i := range blocks {
- str, err := downloadFile(ctx, nodes[i], blocks[i].FileHash)
- if err != nil {
- for i -= 1; i >= 0; i-- {
- fileStrs[i].Close()
- }
- return nil, fmt.Errorf("donwloading file: %w", err)
- }
-
- fileStrs = append(fileStrs, str)
- }
-
- fileReaders, filesCloser := myio.ToReaders(fileStrs)
-
- var indexes []int
- for _, b := range blocks {
- indexes = append(indexes, b.Index)
- }
-
- outputs, outputsCloser := myio.ToReaders(rs.ReconstructData(fileReaders, indexes))
- return myio.AfterReadClosed(myio.Length(myio.ChunkedJoin(outputs, int(iter.ecInfo.ChunkSize)), fileSize), func(c io.ReadCloser) {
- filesCloser()
- outputsCloser()
- }), nil
- }
|