V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
Licsber
V2EX  ›  Go 编程语言

Go 的 http.Get 是阻塞的吗?还是说有超时呢?求问

  •  1
     
  •   Licsber ·
    licsber · 2018-12-08 19:26:32 +08:00 · 4621 次点击
    这是一个创建于 2210 天前的主题,其中的信息可能已经有所发展或是发生改变。
    func justRun(userinfo userInfo) bool {
    	r, _ := http.Get("http://localhost:8080/?imeicode=" + userinfo.ImeiCode)
    	var w []byte
    	if r != nil {
    		w, _ = ioutil.ReadAll(r.Body)
    		_ = r.Body.Close()
    	} else {
    		time.Sleep(5 * time.Second)
    		if r != nil {
    			w, _ = ioutil.ReadAll(r.Body)
    			_ = r.Body.Close()
    		} else {
    			r, _ := http.Get("http://localhost:8080/?imeicode=" + userinfo.ImeiCode)
    			time.Sleep(5 * time.Second)
    			if r != nil {
    				w, _ = ioutil.ReadAll(r.Body)
    				_ = r.Body.Close()
    			}
    		}
    	}
    	if len(w) == 4 {
    		return true
    	} else {
    		return false
    	}
    }
    

    看了源码发现 Get 调用的 NewRequest 方法
    但是小白表示没发现什么异常呀 晕了
    这个 get 请求的地址是同服务器的 代码如下

    func handle(w http.ResponseWriter, r *http.Request) {
    	defer r.Body.Close()
    	_ = r.ParseForm()
    	if runCode(r.Form["imeicode"][0]) {
    		_, _ = Fprint(w, "true")
    	} else {
    		_, _ = Fprint(w, "false")
    	}
    }
    

    runCode 的执行时间大概在 1s 以内 不会超过 2 秒
    返回值也是 bool 型
    还有一直没搞懂的是 r.Body.Close()这个到底是干啥用的
    因为服务端调用这个之后 朝着 w 写东西 客户端也能收到
    客户端调用这个之后 貌似什么都不会发生?

    20 条回复    2018-12-12 22:21:25 +08:00
    Licsber
        1
    Licsber  
    OP
       2018-12-08 19:45:08 +08:00
    为啥我的代码会写的这么丑呢..
    求解答
    yufpga
        2
    yufpga  
       2018-12-08 19:59:16 +08:00 via Android
    是同步阻塞的,r.Body 必须显式的 close 掉,不然,不断有请求,但资源没释放,程序很快会崩掉的。你上面那段代码问题很多,首先要检察 err,而不是去判断 r 是否是 nil。还有没有关闭 r.Body
    yufpga
        3
    yufpga  
       2018-12-08 20:01:41 +08:00 via Android
    哦,看错了,有 close,还是看 golang 标准库中的例子吧,挺别扭的
    mengyaoss77
        4
    mengyaoss77  
       2018-12-08 20:05:19 +08:00 via Android
    好好利用 error 啊,少用 else 多用提前 return.
    Licsber
        5
    Licsber  
    OP
       2018-12-08 20:05:34 +08:00
    @yufpga 也就是说 http.Get 这个方法不会异步执行是吗
    检查了 err 是 connect time out .. 这个超时是哪里规定的呢
    他不会一直等待我的服务端 r.Body.Close()吗
    我在想这个要不要改成 RPC 会不会好一点呢
    Licsber
        6
    Licsber  
    OP
       2018-12-08 20:07:32 +08:00
    @mengyaoss77 我觉得最好玩的就是 如果没有响应(r == nil)的话
    我再延时 5s 居然就能读取了
    所以我就有一种 Get 是异步执行的错觉
    而且服务端的那段代码理论上不会执行很久
    就很奇怪
    yufpga
        7
    yufpga  
       2018-12-08 20:26:30 +08:00 via Android
    http.Get 对于调用者来说自然是同步阻塞的,对于失败的请求,你自然不需要去调用 r.Body.Close,标准库已经帮你做了,timeout 是因为标准库内部使用了 context,控制了某一次请求的生命周期,http.Get 其实是使用了 DefaultClient, 如果需要自己设置这个超时时间,你需要设置 client 的 Timeout 属性。多看源码吧。
    Licsber
        8
    Licsber  
    OP
       2018-12-08 20:40:31 +08:00
    @yufpga 好的 谢谢!🙏
    azzwacb9001
        9
    azzwacb9001  
       2018-12-08 22:12:26 +08:00
    @yufpga 您好,我想请问一下,如果我在一个 for 循环中调用 http.Get (不使用 goroutine ),这些 http 请求是会逐个执行,还是并发执行呢?
    Licsber
        10
    Licsber  
    OP
       2018-12-08 22:19:18 +08:00
    @azzwacb9001 我觉得当然是逐个执行呀 想想 for 循环里随便一个表达式 只要不加 go 都是执行完上一条才下一条
    azzwacb9001
        11
    azzwacb9001  
       2018-12-08 22:19:33 +08:00
    无法回答楼主的问题,但是顺道提一下,最好不要这么写

    ```
    if r != nil {
    w, _ = ioutil.ReadAll(r.Body)
    _ = r.Body.Close()
    } else {
    time.Sleep(5 * time.Second)
    if r != nil {
    w, _ = ioutil.ReadAll(r.Body)
    _ = r.Body.Close()
    } else {
    r, _ := http.Get("http://localhost:8080/?imeicode=" + userinfo.ImeiCode)
    time.Sleep(5 * time.Second)
    if r != nil {
    w, _ = ioutil.ReadAll(r.Body)
    _ = r.Body.Close()
    }
    }
    }
    ```

    而是这样写

    ```
    if r == nil {
    w, _ = ioutil.ReadAll(r.Body)
    _ = r.Body.Close()
    return false
    }

    // 正确情况的逻辑

    }
    ```

    也就是说,尽量不要用 if-else 的方式处理错误,在 if 中处理错误即可。具体可以参考 effective go。
    还有就是,重试最好也别写在错误处理里,万一你要重试 10 次咋办?
    azzwacb9001
        12
    azzwacb9001  
       2018-12-08 22:20:09 +08:00
    ....v2ex 不能使用 markdown 回复吗?
    azzwacb9001
        13
    azzwacb9001  
       2018-12-08 22:20:52 +08:00
    @Licsber 按理来说是这样,但上次我朋友跟我说 http.Get 在内部自带 goroutine 机制。我验证一下吧。
    Licsber
        14
    Licsber  
    OP
       2018-12-08 22:32:46 +08:00
    @azzwacb9001 谢谢! 我在试着用 RPC 重新写这一段代码 实在太丑了 而且 http 的超时我也没找到在哪....尴尬
    Licsber
        15
    Licsber  
    OP
       2018-12-08 22:33:16 +08:00
    @azzwacb9001 内部自带这个是指的服务端吧? 我觉得客户端没必要啊 并发打服务器玩吗?
    azzwacb9001
        16
    azzwacb9001  
       2018-12-08 22:44:44 +08:00
    @Licsber 是的我验证了一下,的确是顺序执行的 = =
    goofool
        17
    goofool  
       2018-12-11 10:22:36 +08:00
    server 端的 request 不需要自己 close
    Licsber
        18
    Licsber  
    OP
       2018-12-12 15:31:24 +08:00
    @goofool 是这样, 如果我想关闭这个链接该怎么办呢. 比如用户填写表单错误的时候要让代码不要向下执行, 谢谢.
    goofool
        19
    goofool  
       2018-12-12 15:55:56 +08:00 via Android
    @Licsber 额,return 就可以了
    Licsber
        20
    Licsber  
    OP
       2018-12-12 22:21:25 +08:00
    @goofool 啊哈 .
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2991 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 11:10 · PVG 19:10 · LAX 03:10 · JFK 06:10
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.