我编写了一个简单的程序来获取url列表以将它们存储在某些文件中。在这个例子中谷歌和Gmail。我总是在不同的软件版本中运行相同的命令。程序存储在goFetchAll中:这是算法的编译版本的名称。
0.23s 0 http://www.google.com 1.15s 0 http://www.gmail.com
第二个数字应该是内容的字节数。但它总是0。
package main
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"strings"
"time"
)
func main() {
start := time.Now()
ch := make(chan string)
for _, url := range os.Args[1:] {
go fetch(url, ch)
}
for range os.Args[1:] {
fmt.Println(<-ch)
}
secs := time.Since(start).Seconds()
fmt.Sprintf("%.2fs elapsed\n", secs)
}
func fetch(url string, ch chan<- string) {
start := time.Now()
resp, err := http.Get(url)
if err != nil {
ch <- fmt.Sprint(err)
return
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
ch <- fmt.Sprintf("Cant catch content")
return
}
nbytes, err := io.Copy(ioutil.Discard, resp.Body)
defer resp.Body.Close()
if err != nil {
ch <- fmt.Sprintf("while reading %s: %v", url, err)
return
}
secs := time.Since(start).Seconds()
ch <- fmt.Sprintf("%.2fs %7d %s", secs, nbytes, url)
// store on file
filename := string(url)
filename = strings.Replace(filename, ":", "", -1)
filename = strings.Replace(filename, "//", "-", -1)
filename = strings.Replace(filename, "/", "", -1)
filename = strings.Replace(filename, ".", "-", -1)
filename = "downloads/" + filename + ".html"
f, err := os.Create(filename)
f.Write(body)
defer f.Close()
if err != nil {
ch <- fmt.Sprintf("while writing %s: %v", url, err)
return
}
}
我还有一个实际可行的相同脚本的旧版本:
0.25s 10363 http://www.google.com 0.89s 66576 http://www.gmail.com
package main
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"time"
)
func main() {
start := time.Now()
ch := make(chan string)
for _, url := range os.Args[1:] {
go fetch(url, ch)
}
for range os.Args[1:] {
fmt.Println(<-ch)
}
fmt.Println("%.2fs elapsed\n", time.Since(start).Seconds())
}
func fetch(url string, ch chan<- string) {
start := time.Now()
resp, err := http.Get(url)
if err != nil {
ch <- fmt.Sprint(err)
return
}
nbytes, err := io.Copy(ioutil.Discard, resp.Body)
resp.Body.Close()
if err != nil {
ch <- fmt.Sprintf("whioe reading %s: %v", url, err)
return
}
secs := time.Since(start).Seconds()
ch <- fmt.Sprintf("%.2fs %7d %s", secs, nbytes, url)
}
有人可以解释为什么最新版本总是算0秒?
我的部分解决方案如下。我只是再次请求http.Get(url)
resp, err := http.Get(url)
nbytes, err := io.Copy(ioutil.Discard, resp.Body)
defer resp.Body.Close() // dont leak resources
if err != nil {
ch <- fmt.Sprintf("while reading %s: %v", url, err)
return
}
resp, err = http.Get(url)
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
ch <- fmt.Sprintf("Cant catch content")
return
}
答案 0 :(得分:1)
之所以这样,是因为您已经在该通话时读过一次响应。所以第二次,从流中读取0个字节。删除错误检查调用后:
resp, err := http.Get(url)
body, err := ioutil.ReadAll(resp.Body)
nbytes, err := io.Copy(ioutil.Discard, resp.Body)
请注意第二行的ReadAll
电话。
我想提出的另一个小建议(与实际问题无关)是在初始化流后立即使用defer
来电。例如:
resp, err := http.Get(url)
if err != nil {
ch <- fmt.Sprint(err)
return
}
defer resp.Body.Close()
虽然没有具体提及,但可以从this section in Effective Go推断出来。在这里解释:
其次,这意味着关闭位于开放附近,这比将其放置在函数末尾要清晰得多。