Oxy
Oxy é uma biblioteca Go que fornece manipuladores HTTP (handlers) para aprimorar a biblioteca padrão HTTP.
Funcionalidades Principais
- Buffer: Realiza novas tentativas e armazena buffers de requisições e respostas.
- Stream: Encaminha requisições diretaemnte, suportando codificação chunked com intervalo de flush configurável.
- Forward: Encaminha requisições para um destino remoto e reescreve os cabeçalhos.
- Roundrobin: Implementa um balanceador de carga round-robin.
- Circuit Breaker: Disjuntor de circuito no estilo Hystrix.
- Connlimit: Limitador de conexões simultâneas.
- Ratelimit: Limitador de taxa de requisições (baseado no algoritmo de balde de tokken).
- Trace: Logger estruturado para requisições e respostas.
Exemplo: Forward
O componente Forward atua como um proxy reverso. A lógica central adapta a solicitação para o destino correto.
// adapta a requisição para o URL de destino
func (f *encaminhadorHTTP) adaptarSolicitacao(reqSaida *http.Request, destino *url.URL) {
reqSaida.URL = utils.CopiarURL(reqSaida.URL)
reqSaida.URL.Scheme = destino.Scheme
reqSaida.URL.Host = destino.Host
u := f.obterURLDaRequisicao(reqSaida)
reqSaida.URL.Path = u.Path
reqSaida.URL.RawPath = u.RawPath
reqSaida.URL.RawQuery = u.RawQuery
reqSaida.RequestURI = "" // Requisições de saída não devem ter RequestURI
reqSaida.Proto = "HTTP/1.1"
reqSaida.ProtoMajor = 1
reqSaida.ProtoMinor = 1
if f.reescritor != nil {
f.reescritor.Reescrever(reqSaida)
}
// Não passa o cabeçalho Host do cliente, a menos que PassHostHeader esteja definido.
if !f.passarHost {
reqSaida.Host = destino.Host
}
}
proxyReverso := httputil.ReverseProxy{
Director: func(req *http.Request) {
f.adaptarSolicitacao(req, reqEntrada.URL)
},
Transport: f.roundTripper,
FlushInterval: f.intervaloDeFlush,
ModifyResponse: f.modificarResposta,
BufferPool: f.poolDeBuffer,
}
proxyReverso.ServeHTTP(w, reqSaida)
Exemplo: Ratelimit
O componente Ratelimit controla a taxa de requisições por fonte (ex.: andereço IP) usando um balde de tokens.
func (lt *LimitadorPorToken) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// Extrai informações do cliente, como IP e host.
identificadorCliente, quantidade, err := lt.extrator.Extrair(req)
if err != nil {
lt.tratadorDeErros.ServeHTTP(w, req, err)
return
}
// Processa o consumo da taxa permitida.
if err := lt.processarConsumoDeTaxas(req, identificadorCliente, quantidade); err != nil {
lt.logger.Warnf("Requisição limitada %v %v, limite: %v", req.Method, req.URL, err)
lt.tratadorDeErros.ServeHTTP(w, req, err)
return
}
lt.proximo.ServeHTTP(w, req)
}
func (lt *LimitadorPorToken) processarConsumoDeTaxas(req *http.Request, identificador string, quantidade int64) error {
lt.mutex.Lock()
defer lt.mutex.Unlock()
taxasEfetivas := lt.resolverTaxas(req)
baldeI, existe := lt.baldes.Get(identificador)
var balde *ConjuntoDeBaldeDeToken
if existe {
balde = baldeI.(*ConjuntoDeBaldeDeToken)
balde.Atualizar(taxasEfetivas)
} else {
balde = NovoConjuntoDeBaldeDeToken(taxasEfetivas, lt.relogio)
// Define um TTL de 10 vezes o período da taxa.
lt.baldes.Set(identificador, balde, int(balde.periodoMaximo/time.Second)*10+1)
}
atraso, err := balde.Consumir(quantidade)
if err != nil {
return err
}
if atraso > 0 {
return &ErroTaxaMaxima{Atraso: atraso}
}
return nil
}