请教个问题:一次调用为何被执行了两三次?

wangfeiping · 2016-05-28 11:54:01 · 2266 次点击 · 大约8小时之前 开始浏览    置顶
这是一个创建于 2016-05-28 11:54:01 的主题,其中的信息可能已经有所发展或是发生改变。

日志

[Debug] 2016-05-28 19:47:10.812379321 +0800 CST get file handler:  GET   /16,2019fe6c23
[Debug] 2016-05-28 19:47:10.967188428 +0800 CST get file handler:  GET   /16,2019fe6c23

简要代码

    http.HandleFunc("/submit", submitHandler)
    http.HandleFunc("/", getFileHandler)
    e := http.ListenAndServe(listeningAddress, nil)
    if e != nil {
        log.Error("Weeder Proxy startup error: ", e)
    }

getFileHandler 方法中通过http.Get(url) 读取了另外一个网址的图片,然后返回给当前调用的客户端, 奇怪的是从日志看为什么每次调用都会被执行两三次?

请问有人遇到过这个问题么? 先谢谢各位了。

源码

package main

import (
    "bytes"
    log "fmt"
    "io"
    "io/ioutil"
    "net/http"
)

func main() {
    http.HandleFunc("/submit", submitHandler)
    http.HandleFunc("/", getFileHandler)
    e := http.ListenAndServe("0.0.0.0:9330", nil)
    if e != nil {
        log.Println("Weeder Proxy startup error: ", e)
    }
}

func submitHandler(w http.ResponseWriter, r *http.Request) {
    log.Println("submit handler: ", r.Method, " ", r.RequestURI)
}

func getFileHandler(w http.ResponseWriter, r *http.Request) {
    log.Println("get file handler: ", r.Method, " ", r.RequestURI)
    err := download("https://www.baidu.com/img/bd_logo1.png", w, r)
    //    var err error = nil
    if err != nil {
        log.Println("download: ", err)
        w.WriteHeader(http.StatusBadRequest)
        w.Write([]byte("{\"error\":\"Can't get the file!\"}"))
    } else {
        log.Println("get file handler: ", r.Method, " ", r.RequestURI, "success.")
    }
}

func download(url string, w http.ResponseWriter, r *http.Request) error {
    defer func() {
        if r := recover(); r != nil {
            log.Println("download: ", r)
        }
    }()
    resp, err := http.Get(url)
    if err != nil {
        return err
    }
    defer resp.Body.Close()
    /**
     * ioutil.ReadAll(resp.Body)
     * 会将文件整个读取到内存中后才返回,如果文件较大或并发访问量较大,需要注意
     */
    pix, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return err
    }
    return writeResponseContent(bytes.NewReader(pix), resp, w, r)
    //    pix, err := ioutil.ReadFile("/apps/gopath/src/qianbao.com/weeder/explain-it-simply.jpg")
    //    if err != nil {
    //        return err
    //    }
    //    var resp *http.Response = nil
    //    return ps.writeResponseContent(bytes.NewReader(pix), int64(len(pix)), resp, w, r)
}

func writeResponseContent(
    rs io.ReadSeeker, resp *http.Response,
    w http.ResponseWriter, r *http.Request) error {
    var e error = nil
    mimeType := ""
    if resp != nil {
        mimeType = resp.Header.Get("Content-Type")
        w.Header().Set("Content-Type", mimeType)
        w.Header().Set("Content-Disposition", resp.Header.Get("Content-Disposition"))
        w.Header().Set("Content-Length", resp.Header.Get("Content-Length"))
    }
    w.Header().Set("Accept-Ranges", "bytes")
    rangeReq := r.Header.Get("Range")
    if rangeReq == "" {
        _, e = io.Copy(w, rs)
        return e
    }
    return e
}

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2266 次点击  
加入收藏 微博
7 回复  |  直到 2016-05-30 03:08:01
blov
blov · #1 · 9年之前

你是通过什么方式调用的?确认不是调用了多次?另外,日志是在 handler 中入口就打印?

wangfeiping
wangfeiping · #2 · 9年之前
blovblov #1 回复

你是通过什么方式调用的?确认不是调用了多次?另外,日志是在 handler 中入口就打印?

就是浏览器调用,日志是在 handler 中入口就打印,测试中,如果去掉handler中调用的http.Get就正常了

blov
blov · #3 · 9年之前

那就是说不是handler会执行多次,而是 http.Get 会执行多次?如果这样,你怎么确认 Get 执行多次的?这块相关的代码贴出来看看

wangfeiping
wangfeiping · #4 · 9年之前
blovblov #3 回复

那就是说不是handler会执行多次,而是 http.Get 会执行多次?如果这样,你怎么确认 Get 执行多次的?这块相关的代码贴出来看看

代码已发,是handler执行多次,是在http.Get之前输出的日志

blov
blov · #5 · 9年之前

我用你代码测试,不会执行两次啊。

get file handler:  GET   /
get file handler:  GET   / success.

输出这两次,但这两次是你在 getFileHandler 中开始和结尾各输出了一次!

wangfeiping
wangfeiping · #6 · 9年之前
blovblov #5 回复

我用你代码测试,不会执行两次啊。 ``` get file handler: GET / get file handler: GET / success. ``` 输出这两次,但这两次是你在 getFileHandler 中开始和结尾各输出了一次!

确认问题了,返回的header信息中没有设置 Last-Modified 的话,firefox浏览器会调用多次。

blov
blov · #7 · 9年之前

所以,我一开始就问是什么方式调用的。应该使用 curl 之类的命令行方式确认。 :smile:

添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传