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

2018-12-08 19:26:32 +08:00
 Licsber
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 写东西 客户端也能收到
客户端调用这个之后 貌似什么都不会发生?

4458 次点击
所在节点    Go 编程语言
20 条回复
Licsber
2018-12-08 19:45:08 +08:00
为啥我的代码会写的这么丑呢..
求解答
yufpga
2018-12-08 19:59:16 +08:00
是同步阻塞的,r.Body 必须显式的 close 掉,不然,不断有请求,但资源没释放,程序很快会崩掉的。你上面那段代码问题很多,首先要检察 err,而不是去判断 r 是否是 nil。还有没有关闭 r.Body
yufpga
2018-12-08 20:01:41 +08:00
哦,看错了,有 close,还是看 golang 标准库中的例子吧,挺别扭的
mengyaoss77
2018-12-08 20:05:19 +08:00
好好利用 error 啊,少用 else 多用提前 return.
Licsber
2018-12-08 20:05:34 +08:00
@yufpga 也就是说 http.Get 这个方法不会异步执行是吗
检查了 err 是 connect time out .. 这个超时是哪里规定的呢
他不会一直等待我的服务端 r.Body.Close()吗
我在想这个要不要改成 RPC 会不会好一点呢
Licsber
2018-12-08 20:07:32 +08:00
@mengyaoss77 我觉得最好玩的就是 如果没有响应(r == nil)的话
我再延时 5s 居然就能读取了
所以我就有一种 Get 是异步执行的错觉
而且服务端的那段代码理论上不会执行很久
就很奇怪
yufpga
2018-12-08 20:26:30 +08:00
http.Get 对于调用者来说自然是同步阻塞的,对于失败的请求,你自然不需要去调用 r.Body.Close,标准库已经帮你做了,timeout 是因为标准库内部使用了 context,控制了某一次请求的生命周期,http.Get 其实是使用了 DefaultClient, 如果需要自己设置这个超时时间,你需要设置 client 的 Timeout 属性。多看源码吧。
Licsber
2018-12-08 20:40:31 +08:00
@yufpga 好的 谢谢!🙏
azzwacb9001
2018-12-08 22:12:26 +08:00
@yufpga 您好,我想请问一下,如果我在一个 for 循环中调用 http.Get (不使用 goroutine ),这些 http 请求是会逐个执行,还是并发执行呢?
Licsber
2018-12-08 22:19:18 +08:00
@azzwacb9001 我觉得当然是逐个执行呀 想想 for 循环里随便一个表达式 只要不加 go 都是执行完上一条才下一条
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
2018-12-08 22:20:09 +08:00
....v2ex 不能使用 markdown 回复吗?
azzwacb9001
2018-12-08 22:20:52 +08:00
@Licsber 按理来说是这样,但上次我朋友跟我说 http.Get 在内部自带 goroutine 机制。我验证一下吧。
Licsber
2018-12-08 22:32:46 +08:00
@azzwacb9001 谢谢! 我在试着用 RPC 重新写这一段代码 实在太丑了 而且 http 的超时我也没找到在哪....尴尬
Licsber
2018-12-08 22:33:16 +08:00
@azzwacb9001 内部自带这个是指的服务端吧? 我觉得客户端没必要啊 并发打服务器玩吗?
azzwacb9001
2018-12-08 22:44:44 +08:00
@Licsber 是的我验证了一下,的确是顺序执行的 = =
goofool
2018-12-11 10:22:36 +08:00
server 端的 request 不需要自己 close
Licsber
2018-12-12 15:31:24 +08:00
@goofool 是这样, 如果我想关闭这个链接该怎么办呢. 比如用户填写表单错误的时候要让代码不要向下执行, 谢谢.
goofool
2018-12-12 15:55:56 +08:00
@Licsber 额,return 就可以了
Licsber
2018-12-12 22:21:25 +08:00
@goofool 啊哈 .

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/515700

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX