Go net/http Client 的某些参数不是并发安全的?

2019-11-25 16:16:13 +08:00
 wnanbei

在官方文档中有提到 net/http client 是协程安全的,应该复用。

The Client's Transport typically has internal state (cached TCP connections), so Clients should be reused instead of created as needed. Clients are safe for concurrent use by multiple goroutines.

但使用 Client 发起请求时,有一部分请求的设置是以函数或字段的方式放在 Client 的参数中的。例如 Proxy 代理、重定向检查、超时设置。要设置的话必须像以下这般设置:

func RedirectFunc(req *http.Request, via []*http.Request) error {
	if len(via) > 5 {
		err := &RedirectError{r}
		return WrapErr(err, "RedirectError")
	}
	return nil
}

client.CheckRedirect = RedirectFunc  // 设置重定向

这样的话就会造成一个问题,在并发的过程中如果要更改重定向次数的话,就会有并发安全问题,设置 Proxy 代理和超时时间也有这个问题。

比如在这样一个假设情况中,我现在有 10000 个请求需要并发,每个请求需要设置不同的特定 Proxy 代理。那么这时候使用全局的 Client,在每个协程中更改 client.CheckRedirect 函数,然后发起请求,显然会有并发问题,发起请求时使用的并不一定是指定的那个 Proxy。

想了想解决的办法:

  1. 每个请求新建一个 Client ?
  2. 把更改参数和请求一起加锁锁起来?

请问这种情况有靠谱的解决方法吗?

6699 次点击
所在节点    Go 编程语言
27 条回复
winterssy
2019-11-26 21:46:16 +08:00
这个问题我之前在基于 net/http 开发一个网络请求库的时候也思考过,能不能做到想 python requests 库那样为每一个 HTTP 请求单独设置证书校验逻辑、代理等控制策略,而且做到并发安全。

可 go 的方案就是 client 只初始化 1 次就好,之后不应该再修改,要实现我想要的功能就必须动态修改 client,文档告诉我们 client 是并发安全的,即使你在多个 goroutines 修改 client 也不会出现数据竞争,但 client 在发起请求时只会使用最近修改的值,并不能达到我所期望的效果(每个请求对应不同的策略)。

我想到可行的方法就是为每一个请求克隆一个 client,但这样势必会影响性能。最后一刀切,凡是修改 http client 的我都没提供 api 去简化,有这种需求的话创建不同的 client 就是。

18 楼提到 context 我觉得也是不可行的,因为本质上你还是要动态修改 client,反而额外增加了类型断言的开销。
icexin
2019-11-27 00:45:19 +08:00
@winterssy 这些函数本身就接受一个 Request 对象来根据不同的请求做出不同的处理策略,函数就赋值一次,为什么说修改 client 了?
winterssy
2019-11-27 00:54:08 +08:00
@icexin #22 因为 net/http 设置代理 /重定向策略都是在 client 设置的,即便你在 Request 对象上下文携带了相关参数,你要发起请求就要经过 client。
icexin
2019-11-27 01:04:51 +08:00
@winterssy 是一个 client,但各个请求是独立处理的
winterssy
2019-11-27 01:18:35 +08:00
@icexin #24 各个请求是独立处理,但他们是共用一个 client,多个 gourotines 并发去请求(多核的话可以并行),同一个 client 同一时刻可以也有不同的参数?退一步来说,如果这可行的话不用 context 就能做到,何必多此一举。
icexin
2019-11-27 10:42:10 +08:00
winterssy
2019-11-27 10:56:20 +08:00
@icexin #26 感谢,这个对 Proxy,CheckRedirect 可行,因为它们跟 request 关联,对于 transport 其它跟 request 没有关联的参数就无解了,比如像 python requests 的 verify

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

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

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

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

© 2021 V2EX