Golang中如何处理HTTP响应最佳实践

Golang中如何处理HTTP响应最佳实践 一个标准的 HTTP 请求如下:

	data, _ := json.Marshal(map[string]any{
		"passwd": "123",
		"name":   "xx",
	})
	req, err := http.Post("http://localhost:8085/login", "application/json", bytes.NewReader(data))
	if err != nil {
		panic(err)
	}
	defer req.Body.Close()
	io.Copy(io.Discard, req.Body) // 必须这样做吗?

我需要发起很多 HTTP 请求。如果我不关心响应,我应该使用 io.Copy(io.Discard, req.Body) 吗? 我看到一些文章说 resp.Body.Close() 会读取并丢弃剩余的响应体数据。


更多关于Golang中如何处理HTTP响应最佳实践的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

直接关闭 body 即可,不必想太多。

更多关于Golang中如何处理HTTP响应最佳实践的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go中处理HTTP响应时,确保正确关闭响应体是关键。以下是处理HTTP响应的最佳实践:

1. 必须关闭响应体

即使不关心响应内容,也必须调用resp.Body.Close()

resp, err := http.Post("http://localhost:8085/login", "application/json", bytes.NewReader(data))
if err != nil {
    panic(err)
}
defer resp.Body.Close()  // 必须调用

2. 完全读取响应体

如果不读取响应体,连接可能无法被复用。以下是正确做法:

// 方法1:使用io.Copy完全读取
resp, err := http.Post(url, contentType, body)
if err != nil {
    return err
}
defer resp.Body.Close()

_, err = io.Copy(io.Discard, resp.Body)
if err != nil {
    return err
}

// 方法2:使用io.ReadAll(Go 1.16+)
resp, err := http.Post(url, contentType, body)
if err != nil {
    return err
}
defer resp.Body.Close()

bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
    return err
}
_ = bodyBytes // 如果不关心内容

3. 批量请求的优化方案

对于大量HTTP请求,建议:

func makeRequest(url string, data []byte) error {
    resp, err := http.Post(url, "application/json", bytes.NewReader(data))
    if err != nil {
        return err
    }
    defer resp.Body.Close()
    
    // 必须完全读取响应体
    _, err = io.Copy(io.Discard, resp.Body)
    return err
}

// 使用sync.Pool复用缓冲区
var bufferPool = sync.Pool{
    New: func() interface{} {
        return bytes.NewBuffer(make([]byte, 0, 4096))
    },
}

func makeRequestWithPool(url string, data map[string]any) error {
    buf := bufferPool.Get().(*bytes.Buffer)
    defer bufferPool.Put(buf)
    defer buf.Reset()
    
    json.NewEncoder(buf).Encode(data)
    
    resp, err := http.Post(url, "application/json", buf)
    if err != nil {
        return err
    }
    defer resp.Body.Close()
    
    _, err = io.Copy(io.Discard, resp.Body)
    return err
}

4. 使用http.Client控制连接

对于高频请求,配置自定义的http.Client:

client := &http.Client{
    Transport: &http.Transport{
        MaxIdleConns:        100,
        MaxIdleConnsPerHost: 10,
        IdleConnTimeout:     90 * time.Second,
    },
    Timeout: 10 * time.Second,
}

func makeRequestWithClient(client *http.Client, url string, data []byte) error {
    resp, err := client.Post(url, "application/json", bytes.NewReader(data))
    if err != nil {
        return err
    }
    defer resp.Body.Close()
    
    _, err = io.Copy(io.Discard, resp.Body)
    return err
}

5. 错误处理完整示例

func sendLoginRequest(username, password string) error {
    data, err := json.Marshal(map[string]string{
        "name":   username,
        "passwd": password,
    })
    if err != nil {
        return fmt.Errorf("marshal failed: %w", err)
    }
    
    resp, err := http.Post("http://localhost:8085/login", 
        "application/json", 
        bytes.NewReader(data))
    if err != nil {
        return fmt.Errorf("request failed: %w", err)
    }
    defer resp.Body.Close()
    
    // 检查HTTP状态码
    if resp.StatusCode != http.StatusOK {
        body, _ := io.ReadAll(resp.Body)
        return fmt.Errorf("server returned %d: %s", 
            resp.StatusCode, string(body))
    }
    
    // 如果不关心响应体但需要保持连接可复用
    _, err = io.Copy(io.Discard, resp.Body)
    if err != nil {
        return fmt.Errorf("reading response failed: %w", err)
    }
    
    return nil
}

关键点:

  1. 必须调用defer resp.Body.Close()
  2. 必须完全读取响应体(使用io.Copy(io.Discard, resp.Body)io.ReadAll
  3. 错误处理要完整
  4. 对于大量请求,使用连接池和缓冲区池优化性能
回到顶部