私はアプリのパフォーマンスを向上させようとしています。 コードの一部は、ファイルをサーバーにチャンクでアップロードします。作業員をスライスして作業する人数を制限します
元のバージョンでは、これが順次ループで行われます。しかし、それは遅く、シーケンス中に各チャンクをアップロードする前に別のサーバと話をする必要があります。
チャンクのアップロードは、単純にゴルーチンに配置することができます。それはうまくいきますが、ソースファイルが非常に大きい場合、大量のメモリを使用するため、良い解決策ではありません。
したがって、バッファリングされたチャネルを使用してアクティブなゴルーチンの数を制限しようとします。ここに私の試みを示すいくつかのコードがあります。私はコンセプトを示すためにそれを取り除き、それを実行して自分のためにテストすることができます。
package main
import (
"fmt"
"io"
"os"
"time"
)
const defaultChunkSize = 1 * 1024 * 1024
// Lets have 4 workers
var c = make(chan int, 4)
func UploadFile(f *os.File) error {
fi, err := f.Stat()
if err != nil {
return fmt.Errorf("err: %s", err)
}
size := fi.Size()
total := (int)(size/defaultChunkSize + 1)
// Upload parts
buf := make([]byte, defaultChunkSize)
for partno := 1; partno <= total; partno++ {
readChunk := func(offset int, buf []byte) (int, error) {
fmt.Println("readChunk", partno, offset)
n, err := f.ReadAt(buf, int64(offset))
if err != nil {
return n, err
}
return n, nil
}
// This will block if there are not enough worker slots available
c <- partno
// The actual worker.
go func() {
offset := (partno - 1) * defaultChunkSize
n, err := readChunk(offset, buf)
if err != nil && err != io.EOF {
return
}
err = uploadPart(partno, buf[:n])
if err != nil {
fmt.Println("Uploadpart failed:", err)
}
<-c
}()
}
return nil
}
func uploadPart(partno int, buf []byte) error {
fmt.Printf("Uploading partno: %d, buflen=%d\n", partno, len(buf))
// Actually upload the part. Lets test it by instead writing each
// buffer to another file. We can then use diff to compare the
// source and dest files.
// Open file. Seek to (partno - 1) * defaultChunkSize, write buffer
f, err := os.OpenFile("/home/matthewh/Downloads/out.tar.gz", os.O_CREATE|os.O_WRONLY, 0755)
if err != nil {
fmt.Printf("err: %s\n", err)
}
n, err := f.WriteAt(buf, int64((partno-1)*defaultChunkSize))
if err != nil {
fmt.Printf("err=%s\n", err)
}
fmt.Printf("%d bytes written\n", n)
defer f.Close()
return nil
}
func main() {
filename := "/home/matthewh/Downloads/largefile.tar.gz"
fmt.Printf("Opening file: %s\n", filename)
f, err := os.Open(filename)
if err != nil {
panic(err)
}
UploadFile(f)
}
ほとんど動作します。しかし、いくつかの問題があります。 1)最後のpartno 22が3回発生しています。正しい長さは実際にはファイルの長さが1MBの倍数でないため612545です。
// Sample output
...
readChunk 21 20971520
readChunk 22 22020096
Uploading partno: 22, buflen=1048576
Uploading partno: 22, buflen=612545
Uploading partno: 22, buflen=1048576
もう一つの問題は、アップロードが失敗する可能性があり、私は行くとどのように最高のゴルーチンの不具合を解決するとともに、十分慣れていませんよ。
最後に、正常に終了すると、uploadPartからデータを戻したいと思います。具体的には、文字列(HTTP ETagヘッダー値)になります。これらのエタグ値は主機能によって収集される必要があります。
このコードをこのインスタンスで構造化するにはどうすればよいでしょうか?私はここで私のニーズを正しく満たす良いゴランのデザインパターンをまだ見つけていません。
コンパイルエラー: 'undefined:UploadChunk'。 – peterSO