CLI中的aws s3 sync
命令可以非常快速地下载大量文件,并且使用AWS Go SDK无法实现相同的性能。我的存储桶中有数百万个文件,因此这对我至关重要。我还需要使用list pages命令,以便添加sync
CLI命令不能很好支持的前缀。
我尝试使用多个goroutine(从10个到1000个)向服务器发出请求,但是与CLI相比,时间要慢得多。每个文件大约需要100毫秒才能运行Go GetObject
函数,这对于我拥有的文件数量来说是不可接受的。我知道AWS CLI在后端也使用Python SDK,所以它的性能如何更好(我在boto和Go中都尝试了我的脚本)。
我正在使用ListObjectsV2Pages
和GetObject
。我的区域与S3服务器的区域相同。
logMtx := &sync.Mutex{}
logBuf := bytes.NewBuffer(make([]byte, 0, 100000000))
err = s3c.ListObjectsV2Pages(
&s3.ListObjectsV2Input{
Bucket: bucket,
Prefix: aws.String("2019-07-21-01"),
MaxKeys: aws.Int64(1000),
},
func(page *s3.ListObjectsV2Output, lastPage bool) bool {
fmt.Println("Received", len(page.Contents), "objects in page")
worker := make(chan bool, 10)
for i := 0; i < cap(worker); i++ {
worker <- true
}
wg := &sync.WaitGroup{}
wg.Add(len(page.Contents))
objIdx := 0
objIdxMtx := sync.Mutex{}
for {
<-worker
objIdxMtx.Lock()
if objIdx == len(page.Contents) {
break
}
go func(idx int, obj *s3.Object) {
gs := time.Now()
resp, err := s3c.GetObject(&s3.GetObjectInput{
Bucket: bucket,
Key: obj.Key,
})
check(err)
fmt.Println("Get: ", time.Since(gs))
rs := time.Now()
logMtx.Lock()
_, err = logBuf.ReadFrom(resp.Body)
check(err)
logMtx.Unlock()
fmt.Println("Read: ", time.Since(rs))
err = resp.Body.Close()
check(err)
worker <- true
wg.Done()
}(objIdx, page.Contents[objIdx])
objIdx += 1
objIdxMtx.Unlock()
}
fmt.Println("ok")
wg.Wait()
return true
},
)
check(err)
许多结果如下:
Get: 153.380727ms
Read: 51.562µs
答案 0 :(得分:2)
您是否尝试过使用https://docs.aws.amazon.com/sdk-for-go/api/service/s3/s3manager/?
iter := new(s3manager.DownloadObjectsIterator)
var files []*os.File
defer func() {
for _, f := range files {
f.Close()
}
}()
err := client.ListObjectsV2PagesWithContext(ctx, &s3.ListObjectsV2Input{
Bucket: aws.String(bucket),
Prefix: aws.String(prefix),
}, func(output *s3.ListObjectsV2Output, last bool) bool {
for _, object := range output.Contents {
nm := filepath.Join(dstdir, *object.Key)
err := os.MkdirAll(filepath.Dir(nm), 0755)
if err != nil {
panic(err)
}
f, err := os.Create(nm)
if err != nil {
panic(err)
}
log.Println("downloading", *object.Key, "to", nm)
iter.Objects = append(iter.Objects, s3manager.BatchDownloadObject{
Object: &s3.GetObjectInput{
Bucket: aws.String(bucket),
Key: object.Key,
},
Writer: f,
})
files = append(files, f)
}
return true
})
if err != nil {
panic(err)
}
downloader := s3manager.NewDownloader(s)
err = downloader.DownloadWithIterator(ctx, iter)
if err != nil {
panic(err)
}
答案 1 :(得分:0)
我最终在最初的帖子中解决了我的脚本问题。我尝试了20个goroutine,看来效果很好。在我的笔记本电脑上,与CLI相比,初始脚本肯定比命令行(i7 8线程,16 GB RAM,NVME)慢。但是,在EC2实例上,差异很小,以至于我不值得花时间去进一步优化它。我在与S3服务器相同的区域中使用了c5.xlarge
实例。