2015-03-30 25 views
1

我已經編寫了一個程序,用作samba共享的簡單HTTP接口:用戶向http://proxy.example.com/?path=\\repository\foo\bar.txt發送獲取請求,\\repository\foo\bar.txt通過http.ServeFile(或多或少)。在服務中使用samba文件的悖論性能

然而,表現是糟糕的。我跑了一些基準,結果讓我難堪。對於上下文來說,圖中有三臺機器:samba服務器(文件所在的位置),代理服務器(go程序運行的地方)和最終用戶的機器(我最終希望文件得到的機器)。 Samba服務器和代理服務器位於同一地點,最終用戶相當遙遠。

從samba機器到使用windows拷貝的用戶機器的直接拷貝以〜1.5MB/s運行。這對我來說已經夠好了,而且我在代理服務中的目標是什麼。

不幸的是,來自用戶機器的curl 'http://proxy.example.com/?path=\\repository\foo\bar.txt' > bar.txt時鐘約爲150KB/s。

因此,讓我們看看samba服務器和代理服務器之間是否存在連接問題。從samba服務器到代理服務器的副本看起來像是......約15MB/s。

嗯,也許這是一件事?我會寫一個去程序來衡量傳輸速度。

src, _ := os.Open("\\\\repository\\foo\\bar.txt") 
start := time.Now() 
written, _ := io.Copy(ioutil.Discard, src) 
elapsed := time.Since(start) 
bytesPerSecond := written/int64(elapsed/time.Second) 

當,15MB/s。

好吧,好吧,也許有什麼其他在去代碼導致的問題。遠程啓動代理服務器,啓動IE,轉至http://proxy.example.com/?path=\\repository\foo\bar.txt,15MB/s。

好的,所以我的代碼顯然工作得很好,它必須是代理服務器和最終用戶之間的連接。我會將bar.txt複製到代理服務器,並在url中使用其本地路徑\mycoolfiles\bar.txt。呵呵,1.5MB /秒。

爲了讓事情變得更加奇怪,我恰好有C:\mycoolfiles設置爲網絡共享名爲\\alexscoolfileshttp://proxy.example.com/?path=\\alexscoolfiles\bar.txt時鐘,在敦敦頓,150KB/s。

只是爲了確認這種瘋狂,我改變了圍棋程序分兩步執行:

  1. 複製從共享文件到本地硬盤驅動器
  2. http.SendFile從那裏

瞧,在文件以15MB/s傳輸時暫停一小段時間後,下載以固定的1.5MB /秒開始。

因此,share-> proxy是15MB/s,proxy-> user是1.5MB/s,但share-> proxy-> user是... 150KB/s?比應該慢十倍?除非你和代理在同一臺機器上,那麼它的速度應該和它應該一樣快呢?而且還存在這個問題,即使它只是一個UNC路徑而另一個只是一個本地路徑而被訪問完全相同的文件?

什麼?

請幫忙,我只是不知道。

編輯:所以我的直覺是(正如評論),它與TCP有關。有問題的代碼已被隔離到幾乎只是io.Copy。

  • 我知道,當讀者是samba文件,作者是ioutil.Discard時,我可以獲得最大的吞吐量。
  • 我知道,當閱讀器是本地文件並且writer是http.Response時,無論使用響應的客戶端的帶寬和RRT如何,我都可以獲得最大吞吐量。
  • 我知道,當閱讀器是一個samba文件,作家是一個http.Response,並且連接是本地的,我獲得了最大的吞吐量。
  • 我知道,當讀者是samba文件時,作者是一個http.Response,並且連接不是本地的,我得到了可怕的(〜1/10)吞吐量。

翻看io.Copy,似乎唯一可能導致問題的是讀取samba文件和寫入響應的時間之間的相互作用;一個足夠快的編寫器使得讀取一個samba文件達到最大吞吐量,一個足夠快的讀取器使得http.Response.Write達到最大吞吐量,但是將它們結合使得一切都很糟糕。

什麼是非常有用的是......實際發生了什麼,更重要的是,我怎樣才能讓這個問題消失。

+3

延遲*帶寬產品 – 2015-03-31 00:22:33

+0

該文件的聲音太小,因此您正在測量開銷而不是實際的傳輸速度。 – 2015-03-31 00:46:29

+0

@Harry我一直在測試的文件是50MB,所以我不認爲這是問題所在。 – 2015-03-31 04:15:11

回答

1

在嘗試追蹤到底在哪裏以及在什麼情況下我可以重現問題後,我最終將其歸結爲一行:如果我評論了io.Copy使用ReadFrom(目前爲io/io.go,第358行),我得到了最大吞吐量。

檢查出在哪裏ReadFrom實現(net/http/server.go,線381):

// ReadFrom is here to optimize copying from an *os.File regular file 
// to a *net.TCPConn with sendfile. 

我真的沒有挖任何更深的意志,但我猜這要求net/sendfile_windows.go,它調用的TransmitFile系統調用,以及介於它和我們各種服務器之間的配置不一樣。

的解決辦法是將它傳遞給io.Copy之前複製和粘貼,我從http.ServeFile所需要的東西,然後轉ResponseWriter到io.Writer(或其他):

type writerOnly struct { 
    io.Writer 
} 

//... 
io.Copy(writerOnly{w}, f) 
//...