resty 客户端 - meetbill/chi GitHub Wiki
1 每次请求时 request := client.R(),而不是多个协程共用 client.R() 吧
是的,不建议多个协程共用同一个 client.R() 创建的 *resty.Request 实例,而应该每个请求(或每个协程内的请求)单独通过 client.R() 创建新的 Request 实例。原因如下:
1. *resty.Request 不是为并发复用设计的
client.R() 返回的 *resty.Request 是单次请求的上下文对象,其内部维护了当前请求的状态(如 URL、Body、Header、参数等)。当多个协程同时调用该实例的 SetBody、SetHeader、Post 等方法时,可能导致:
- 状态覆盖:协程 A 设置的 Body 可能被协程 B 覆盖,导致请求内容混乱。
- 竞态条件:虽然 resty 内部可能对部分操作加锁,但官方并未保证 Request 实例的并发安全性,复用存在潜在风险。
2. 正确的做法:每个请求创建独立的 Request
// 1. 全局创建并复用 Client(Client 是协程安全的)
var client = resty.New().
SetTimeout(DefaultHTTPTimeout).
SetLogger(xagent_log.Logger).
// ... 其他客户端配置
// 2. 每个协程/每次请求时,通过 client.R() 创建新的 Request
func sendRequest(url string, data interface{}) (*Response, error) {
// 为当前请求创建独立的 Request 实例
req := client.R()
req.SetBody(data)
req.SetHeader("Content-Type", "application/json")
// 执行请求
resp, err := req.Post(url)
return resp, err
}
// 3. 多协程调用(安全)
for i := 0; i < 10; i++ {
go func() {
sendRequest("https://example.com", map[string]string{"key": "value"})
}()
}
安全性:每个 req 是独立的,状态仅在当前请求中生效,不会被其他协程干扰。 性能:client.R() 是轻量级操作,创建新实例的开销极小,不会影响性能。
3. 为什么 Client 可以共用,而 Request 不行?
*resty.Client:存储全局配置(超时、重试、拦截器等),内部通过同步机制保证协程安全,设计为可被多个请求共享。 *resty.Request:存储单次请求的临时状态(URL、参数、Body 等),这些状态是请求级别的局部信息,无需也不应该被共享。
结论
多个协程不能共用同一个 client.R() 创建的 Request 实例,而应在每个协程或每次请求时通过 client.R() 新建 Request。这样既能保证并发安全,又能避免状态混乱,是 resty 推荐的使用方式。