HTTP客户端
HTTP客户端提供了带有速率限制、重试机制和错误处理的HTTP请求功能。
概述
HTTPClient
是一个增强的HTTP客户端,具有以下特性:
- 速率限制控制
- 自动重试机制
- 请求超时管理
- 错误处理和日志记录
- 线程安全设计
创建HTTP客户端
默认客户端
go
// 使用默认配置
client := cwe.NewHTTPClient()
默认配置:
- 每10秒1个请求
- 最多重试3次
- 重试间隔1秒
- 30秒超时
自定义配置
go
import (
"net/http"
"time"
"github.com/scagogogo/cwe"
)
// 创建自定义HTTP客户端
httpClient := &http.Client{
Timeout: 30 * time.Second,
}
// 创建速率限制器
rateLimiter := cwe.NewHTTPRateLimiter(5 * time.Second)
// 创建带配置的客户端
client := cwe.NewHTTPClientWithOptions(
httpClient, // 基础HTTP客户端
rateLimiter, // 速率限制器
5, // 最大重试次数
2*time.Second, // 重试间隔
)
基本用法
GET请求
go
resp, err := client.Get("https://api.example.com/data")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Printf("响应: %s\n", string(body))
POST请求
go
data := []byte(`{"key": "value"}`)
resp, err := client.Post(
"https://api.example.com/data",
"application/json",
bytes.NewBuffer(data),
)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
自定义请求
go
req, err := http.NewRequest("PUT", "https://api.example.com/data", nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", "Bearer token")
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
速率限制
获取和设置速率限制器
go
// 获取当前速率限制器
limiter := client.GetRateLimiter()
// 调整速率限制
limiter.SetInterval(3 * time.Second)
// 设置新的速率限制器
newLimiter := cwe.NewHTTPRateLimiter(1 * time.Second)
client.SetRateLimiter(newLimiter)
动态调整
go
// 根据响应状态动态调整速率限制
func adaptiveRateLimit(client *cwe.HTTPClient, resp *http.Response) {
limiter := client.GetRateLimiter()
switch resp.StatusCode {
case 429: // Too Many Requests
// 增加间隔时间
currentInterval := limiter.GetInterval()
newInterval := currentInterval * 2
limiter.SetInterval(newInterval)
log.Printf("速率限制调整为: %v", newInterval)
case 200:
// 成功请求,可以适当减少间隔
currentInterval := limiter.GetInterval()
if currentInterval > time.Second {
newInterval := currentInterval / 2
limiter.SetInterval(newInterval)
}
}
}
重试机制
配置重试
go
// 设置重试参数
client.SetMaxRetries(5)
client.SetRetryInterval(3 * time.Second)
自定义重试逻辑
go
// 定义重试条件
func shouldRetry(resp *http.Response, err error) bool {
if err != nil {
// 网络错误,重试
return true
}
// 服务器错误,重试
if resp.StatusCode >= 500 {
return true
}
// 速率限制,重试
if resp.StatusCode == 429 {
return true
}
return false
}
// 带重试的请求
func requestWithRetry(client *cwe.HTTPClient, url string) (*http.Response, error) {
maxRetries := client.GetMaxRetries()
retryInterval := client.GetRetryInterval()
var lastErr error
var resp *http.Response
for i := 0; i <= maxRetries; i++ {
resp, lastErr = client.Get(url)
if lastErr == nil && !shouldRetry(resp, nil) {
return resp, nil
}
if resp != nil {
resp.Body.Close()
}
if i < maxRetries {
time.Sleep(retryInterval * time.Duration(i+1))
}
}
return nil, fmt.Errorf("重试%d次后仍然失败: %v", maxRetries, lastErr)
}
错误处理
错误类型
go
func handleHTTPError(err error) {
switch {
case strings.Contains(err.Error(), "timeout"):
fmt.Println("请求超时")
case strings.Contains(err.Error(), "connection refused"):
fmt.Println("连接被拒绝")
case strings.Contains(err.Error(), "no such host"):
fmt.Println("主机不存在")
case strings.Contains(err.Error(), "rate limit"):
fmt.Println("请求过于频繁")
default:
fmt.Printf("其他错误: %v\n", err)
}
}
响应状态处理
go
func handleResponse(resp *http.Response) error {
switch resp.StatusCode {
case 200:
return nil
case 400:
return errors.New("请求参数错误")
case 401:
return errors.New("未授权")
case 403:
return errors.New("禁止访问")
case 404:
return errors.New("资源不存在")
case 429:
return errors.New("请求过于频繁")
case 500:
return errors.New("服务器内部错误")
case 502:
return errors.New("网关错误")
case 503:
return errors.New("服务不可用")
default:
return fmt.Errorf("未知状态码: %d", resp.StatusCode)
}
}
监控和日志
请求日志
go
type LoggingHTTPClient struct {
*cwe.HTTPClient
logger *log.Logger
}
func NewLoggingHTTPClient(client *cwe.HTTPClient) *LoggingHTTPClient {
return &LoggingHTTPClient{
HTTPClient: client,
logger: log.New(os.Stdout, "[HTTP] ", log.LstdFlags),
}
}
func (c *LoggingHTTPClient) Do(req *http.Request) (*http.Response, error) {
start := time.Now()
c.logger.Printf("请求: %s %s", req.Method, req.URL.String())
resp, err := c.HTTPClient.Do(req)
duration := time.Since(start)
if err != nil {
c.logger.Printf("请求失败: %v (耗时: %v)", err, duration)
return nil, err
}
c.logger.Printf("响应: %d (耗时: %v)", resp.StatusCode, duration)
return resp, nil
}
性能监控
go
type HTTPMetrics struct {
RequestCount int64
SuccessCount int64
ErrorCount int64
TotalDuration time.Duration
AverageDuration time.Duration
mu sync.RWMutex
}
func (m *HTTPMetrics) RecordRequest(duration time.Duration, success bool) {
m.mu.Lock()
defer m.mu.Unlock()
m.RequestCount++
m.TotalDuration += duration
m.AverageDuration = m.TotalDuration / time.Duration(m.RequestCount)
if success {
m.SuccessCount++
} else {
m.ErrorCount++
}
}
func (m *HTTPMetrics) GetStats() (int64, int64, int64, time.Duration) {
m.mu.RLock()
defer m.mu.RUnlock()
return m.RequestCount, m.SuccessCount, m.ErrorCount, m.AverageDuration
}
连接池配置
自定义传输
go
func createCustomTransport() *http.Transport {
return &http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
}
}
// 使用自定义传输创建客户端
httpClient := &http.Client{
Transport: createCustomTransport(),
Timeout: 30 * time.Second,
}
client := cwe.NewHTTPClientWithOptions(
httpClient,
cwe.NewHTTPRateLimiter(time.Second),
3,
time.Second,
)
最佳实践
- 合理设置超时 - 根据网络环境设置适当的超时时间
- 速率限制 - 遵守API提供商的速率限制要求
- 错误处理 - 实现完善的错误处理和重试机制
- 资源管理 - 及时关闭响应体,避免资源泄漏
- 监控日志 - 记录请求日志,便于问题排查