在 Ubuntu 18.04 上使用标准 scp
(版本:1:7.6p1-4ubuntu0.3
),将 110GB 的文件传输到另一台主机大约需要 6-8 分钟。
当使用 Go 的 pkg/sftp
时,它需要两倍的时间:
例子:
package main
import (
"fmt"
"github.com/melbahja/goph"
"golang.org/x/crypto/ssh"
"io"
"log"
"os"
"time"
)
func main() {
auth, err := goph.Key("your/key", "")
if err != nil {
log.Fatalln(err)
}
client, err := goph.NewConn(&goph.Config{
User: "someUser",
Addr: "someHostname",
Port: 22,
Auth: auth,
//Timeout: time.Duration(timeout) * time.Second,
Callback: ssh.InsecureIgnoreHostKey(),
})
if err != nil {
log.Fatalln(err)
}
file := "/some/file"
start := time.Now()
local, err := os.Open(file)
if err != nil {
return
}
defer local.Close()
ftp, err := client.NewSftp()
// VARIATION 1 => ftp, err := client.NewSftp(sftp.MaxPacketUnchecked(1 << 16))
if err != nil {
return
}
defer ftp.Close()
remote, err := ftp.Create(file)
if err != nil {
return
}
defer remote.Close()
/*
VARIATION 1 => buffer := make([]byte, 1 << 16)
VARIATION 1 => _, err = io.CopyBuffer(remote, local, buffer)
*/
_, err = io.Copy(remote, local)
if err != nil {
log.Fatalln(err)
}
duration := time.Since(start)
fmt.Println(duration)
}
注意:我什至尝试使用注释掉的行(参见 VARIATION 1
)增加读取缓冲区的大小(和 tcp 最大数据包大小),但没有任何区别。
关于为什么以及如何加速 Go 等价物的任何想法?
最佳答案
我发现pkg/sftp支持并发上传,并发上传速度和linux的sftp
命令很像。
这里是客户端初始化:
sftpConn, err := sftp.NewClient(sshConn,
sftp.UseConcurrentReads(true),
sftp.UseConcurrentWrites(true),
sftp.MaxConcurrentRequestsPerFile(64),
// Big max packet size can improve throughput.
sftp.MaxPacketUnchecked(128*agentio.KiB),
// Beware of customizing max packet size for download!
// On download, big max packet size can cause "connection lost" error.
)
使用方法如下,注意ReadFrom
:
func sftpUpload(c *sftp.Client, r io.Reader, path string) error {
fp, err := c.Create(path)
if err != nil {
return fmt.Errorf("create destination file: %w", err)
}
defer fp.Close()
_, err = fp.ReadFrom(r)
return err
}
要使用并发上传,reader 的下划线结构必须是 bytes.Reader、io.LimitedReader 或满足以下接口(interface)之一:
- Len() 整数
- 大小() int64
- 统计()(os.FileInfo,错误)
需要确定要上传的字节数。
如果 ReadFrom
无法确定阅读器的大小,它将回退到单线程上传。
查看ReadFrom
源码:
func (f *File) ReadFrom(r io.Reader) (int64, error) {
f.mu.Lock()
defer f.mu.Unlock()
if f.c.useConcurrentWrites {
var remain int64
switch r := r.(type) {
case interface{ Len() int }:
remain = int64(r.Len())
case interface{ Size() int64 }:
remain = r.Size()
case *io.LimitedReader:
remain = r.N
case interface{ Stat() (os.FileInfo, error) }:
info, err := r.Stat()
if err == nil {
remain = info.Size()
}
}
此外,如果您将读取器传递给 ReadFrom,这会产生大量小数据,即使并发时吞吐量也会非常低。
关于go - pkg/sftp 比 Linux SCP 慢得多,为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65726482/