Clientes HTTP, Transporte e Agrupamento de Conexões
O HTTP de saída em Go flui através de http.Client, que delega o gerenciamento de conexões para http.Transport.
Busque em todas as páginas da documentação
O HTTP de saída em Go flui através de http.Client, que delega o gerenciamento de conexões para http.Transport.
Ajustar timeouts e tamanhos de pool previne vazamentos de goroutine, stalls de TLS e exaustão de conexões sob carga.
http.Client é a API de alto nível para enviar requisições.
Seu campo Transport implementa RoundTripper e é responsável pela conexão TCP, TLS, upgrade para HTTP/2 e reutilização de conexões ociosas.
Client.Timeout limita toda a requisição, incluindo a leitura do corpo.
Controle mais fino reside em Transport: DialContext, TLSHandshakeTimeout, ResponseHeaderTimeout e limites de conexão ociosa.
Todo corpo de resposta deve ser fechado para que as conexões retornem ao pool.
Cartão de receita de referência rápida - pronto para copiar e colar.
var apiClient = &http.Client{
Timeout: 10 * time.Second,
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{Timeout: 5 * time.Second, KeepAlive: 30 * time.Second}).DialContext,
TLSHandshakeTimeout: 5 * time.Second,
ResponseHeaderTimeout: 5 * time.Second,
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
},
}
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
resp, err := apiClient.Do(req)
if err != nil { return err }
defer resp.Body.Close()Quando usar isso:
http.Get/http.Post em caminhos de código de produção.package main
import (
"context"
"encoding/json"
"fmt"
"io"
"log"
"net"
"net/http"
"time"
)
type API struct {
base string
client *http.Client
}
func NewAPI(base string) *API {
return &API{
base: base,
client: &http.Client{
Timeout: 8 * time.Second,
Transport: &http.Transport{
DialContext: (&net.Dialer{
Timeout: 3 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
TLSHandshakeTimeout: 3 * time.Second,
ResponseHeaderTimeout: 4 * time.Second,
MaxIdleConns: 50,
MaxIdleConnsPerHost: 8,
IdleConnTimeout: 60 * time.Second,
},
},
}
}
func (a *API) Fetch(ctx context.Context, path string) (map[string]any, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, a.base+path, nil)
if err != nil {
return nil, err
}
req.Header.Set("Accept", "application/json")
resp, err := a.client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
b, _ := io.ReadAll(io.LimitReader(resp.Body, 4096))
return nil, fmt.Errorf("status %d: %s", resp.StatusCode, string(b))
}
var out map[string]any
if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
return nil, err
}
return out, nil
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
api := NewAPI("https://httpbin.org")
data, err := api.Fetch(ctx, "/get")
if err != nil {
log.Fatal(err)
}
fmt.Println(data["url"])
}O que isso demonstra:
http.Client dedicado com ajuste explícito de Transport.NewRequestWithContext para propagação de cancelamento.defer resp.Body.Close() em todos os caminhos de sucesso e erro.Client.Do chama Transport.RoundTrip, que disca ou reutiliza uma conexão ociosa.Body.Close().MaxIdleConnsPerHost limita a reutilização por upstream; conexões excedentes são fechadas após a resposta.| Camada | Campo | Abrange |
|---|---|---|
| Requisição inteira | Client.Timeout | Dial até a leitura do corpo |
| Dial TCP | Dialer.Timeout | Conexão inicial |
| TLS | TLSHandshakeTimeout | Apenas handshake |
| Tempo de processamento do servidor | ResponseHeaderTimeout | Após o envio da requisição, antes dos cabeçalhos |
| Contexto | req.Context() | Cancelamento do chamador |
// Injeta rastreamento ou autenticação sem substituir Transport inteiramente:
type roundTripper struct { base http.RoundTripper }
func (rt roundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
req = req.Clone(req.Context())
req.Header.Set("Authorization", "Bearer "+token)
return rt.base.RoundTrip(req)
}
client.Transport = roundTripper{base: client.Transport}| Parâmetro | Tipo | Descrição |
|---|---|---|
Client.Timeout | time.Duration | Zero significa nenhum timeout geral |
MaxIdleConnsPerHost | int | Padrão 2 em Go mais antigo; aumentar para hosts "quentes" |
IdleConnTimeout | time.Duration | Fecha conexões ociosas agrupadas |
DisableKeepAlives | bool | Força nova conexão por requisição |
http.DefaultClient em servidores - Sem timeout e transporte compartilhado em todo o processo. Correção: Construa clientes no nível do pacote ou injetados por dependência.resp.Body - Vazamentos de conexões até a exaustão do pool. Correção: defer resp.Body.Close() imediatamente após Do bem-sucedido.io.Copy(io.Discard, resp.Body) antes de fechar em caminhos de erro.Client.Timeout - Pode ocultar a necessidade de ResponseHeaderTimeout em servidores lentos. Correção: Camadas de timeouts de transporte para casos de cabeçalho travado.http.DefaultTransport - Afeta todo o processo, incluindo bibliotecas. Correção: Clone os padrões em uma nova struct Transport.| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
resty / heimdall | Auxiliares de retentativa e backoff desejados | Você quer zero dependências |
| Cliente gRPC | Contratos fortes, streaming | O par só fala REST |
net/http + RoundTripper customizado | Controle total, apenas stdlib | Você precisa de um framework de cliente HTTP completo |
| Sidecar de malha de serviço | mTLS e retentativas no plano de dados | Chamadas JSON internas simples são suficientes |
Client.Timeout é fixo por instância de cliente.
O deadline do contexto varia por chamada e se compõe com o cancelamento do pai.
Use ambos: teto do cliente mais orçamento de contexto por requisição.
Os limites ociosos padrão por host são pequenos.
Alto QPS para uma API sem aumentá-lo causa churn excessivo de TCP.
Sim.
Clientes são seguros para uso concorrente após configurados.
Crie um por dependência upstream no início do processo.
Defina TLSNextProto para um mapa vazio em Transport para conexões TLS, ou configure ForceAttemptHTTP2 para falso.
Implemente a lógica de retentativa em um RoundTripper customizado ou em um wrapper do chamador.
Respeite Idempotency-Key e retente apenas métodos seguros ou caminhos conhecidamente idempotentes.
Sim, até 10 redirecionamentos por padrão.
Defina CheckRedirect no cliente para personalizar ou desabilitar.
Use bytes.NewReader com NewRequestWithContext, defina Content-Type e passe o leitor como corpo.
MaxConnsPerHost (Go 1.11+) limita conexões concorrentes por host.
Combine com limitação de taxa no lado do cliente para proteção.
Defina Transport.TLSClientConfig com o certificado do cliente carregado e o pool de CA.
Rotacione certificados através do callback customizado GetClientCertificate.
Sim, se as políticas de timeout corresponderem.
Diferentes valores de Client.Timeout podem compartilhar um Transport.
Envolva RoundTripper para registrar método, URL, status e duração.
Evite registrar corpos com segredos.
A requisição em andamento é abortada e retorna context.Canceled.
Ainda assim, feche qualquer corpo de resposta parcial se Do retornar uma resposta.
Versões da Pilha: Esta página foi escrita para Go 1.26.x (GC padrão Green Tea, go fix modernizers - verifique o patch na compilação), chi (mais recente - verifique na compilação), gin (mais recente - verifique na compilação), echo (mais recente - verifique na compilação), google.golang.org/grpc (mais recente - verifique na compilação), sigs.k8s.io/controller-runtime (mais recente - verifique na compilação), kubebuilder (mais recente - verifique na compilação), tinygo (mais recente - verifique os alvos de placa na compilação), wazero (mais recente - verifique na compilação) e golangci-lint (mais recente - verifique o conjunto de linters na compilação).
Revisado por Chris St. John·Última atualização: 16 de jul. de 2026