编程 go-resty:链式调用写 Go HTTP 客户端,内置重试、熔断与 SSE

2026-09-12 09:49:20

go-resty:链式调用写 Go HTTP 客户端,内置重试、熔断与 SSE

项目:github.com/go-resty/resty · 文档:pkg.go.dev/github.com/go-resty/resty/v3

为什么需要 go-resty

用 Go 标准库的 net/http 发请求,代码往往又长又繁琐。重试、超时、熔断这些功能,标准库也不直接支持;调试 HTTP 请求时,还得手动拼 curl 命令。

一个真实场景:调用 REST API 获取用户信息。

net/http

client := &http.Client{
    Timeout: 10 * time.Second,
}

req, err := http.NewRequest("GET", "https://api.example.com/users/123", nil)
if err != nil {
    return err
}
req.Header.Set("Authorization", "Bearer token123")
req.Header.Set("Content-Type", "application/json")

resp, err := client.Do(req)
if err != nil {
    return err
}
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
    return err
}

var user User
err = json.Unmarshal(body, &user)
if err != nil {
    return err
}

用 go-resty:

client := resty.New()
resp, err := client.R().
    SetHeader("Authorization", "Bearer token123").
    SetResult(&User{}).
    Get("https://api.example.com/users/123")

user := resp.Result().(*User)

代码更短,调用流程也更清楚。常见用途包括:调用第三方 API、微服务间通信、抓取网页数据、编写 API 测试工具。

go-resty 是什么

go-resty 是一个 Go 语言的 HTTP 客户端库。官方描述是:

Simple HTTP, REST, and SSE client library for Go.

它支持 HTTP 请求、REST API、SSE(Server-Sent Events),API 设计简洁。

核心定位

go-resty 不是 HTTP 服务器,也不是 HTTP 代理,而是一个 HTTP 客户端库,用于发送 HTTP 请求。它主要做了几件事:

  1. 封装 net/http
  2. 提供链式调用 API
  3. 自动处理 JSON
  4. 支持重试、熔断、负载均衡

适用的场景包括调用 REST API、微服务通信、爬虫抓取、API 测试等。

核心特性:链式调用和高级功能

链式调用

resp, err := client.R().
    SetHeader("Content-Type", "application/json").
    SetQueryParam("page", "1").
    SetBody(map[string]string{"name": "John"}).
    Post("https://api.example.com/users")

自动重试

client.SetRetryCount(3).
    SetRetryWaitTime(1 * time.Second).
    SetRetryMaxWaitTime(10 * time.Second).
    AddRetryCondition(func(r *resty.Response, err error) bool {
        return r.StatusCode() >= 500
    })

熔断降级

client.SetCircuitBreaker(&resty.CircuitBreakerConfig{
    MaxRequests: 100,
    Interval:    60 * time.Second,
    Timeout:     10 * time.Second,
})

负载均衡

client.SetLoadBalancer(&resty.LoadBalancerConfig{
    Strategy: resty.LBStrategyRoundRobin,
    Backends: []string{
        "https://api1.example.com",
        "https://api2.example.com",
        "https://api3.example.com",
    },
})

这些能力适合不稳定 API 的自动重试、高并发场景下的熔断降级,以及多实例部署时的请求分散。

安装和使用

安装

go get github.com/go-resty/resty/v3

基本使用

GET 请求

package main

import (
    "fmt"
    "github.com/go-resty/resty/v3"
)

func main() {
    client := resty.New()

    resp, err := client.R().
        SetQueryParam("page", "1").
        Get("https://api.example.com/users")
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    fmt.Println("Response:", resp.String())
    fmt.Println("Status:", resp.Status())
}

POST 请求

type User struct {
    Name  string `json:"name"`
    Email string `json:"email"`
}

func main() {
    client := resty.New()

    user := &User{
        Name:  "John Doe",
        Email: "john@example.com",
    }

    resp, err := client.R().
        SetHeader("Content-Type", "application/json").
        SetBody(user).
        Post("https://api.example.com/users")
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    fmt.Println("Status:", resp.Status())
}

自动解析 JSON

type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

func main() {
    client := resty.New()

    var user User
    resp, err := client.R().
        SetResult(&user).
        Get("https://api.example.com/users/123")
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    fmt.Printf("User: %+v\n", user)
}

错误处理

type APIError struct {
    Code    int    `json:"code"`
    Message string `json:"message"`
}

func main() {
    client := resty.New()

    var apiErr APIError
    resp, err := client.R().
        SetError(&apiErr).
        Get("https://api.example.com/users/999")
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    if resp.IsError() {
        fmt.Printf("API Error: %d - %s\n", apiErr.Code, apiErr.Message)
        return
    }

    fmt.Println("Success!")
}

基本流程可以概括为:New()R()Get/PostSetResult() 自动解析 JSON;SetError() 处理 API 错误。

核心功能

go-resty 虽然定位是 HTTP 客户端,但功能覆盖比较全。

功能一:请求和响应中间件

// 请求中间件
client.OnBeforeRequest(func(c *resty.Client, r *resty.Request) error {
    r.SetHeader("X-Request-ID", generateRequestID())
    return nil
})

// 响应中间件
client.OnAfterResponse(func(c *resty.Client, resp *resty.Response) error {
    log.Printf("Request: %s %s, Status: %s",
        resp.Request.Method,
        resp.Request.URL,
        resp.Status())
    return nil
})

功能二:超时控制

client.SetTimeout(30 * time.Second)
client.SetTransport(&http.Transport{
    DialContext: (&net.Dialer{
        Timeout: 5 * time.Second,
    }).DialContext,
})

功能三:代理设置

client.SetProxy("http://proxy.example.com:8080")
client.RemoveProxy()
client.SetCookies([]*http.Cookie{
    {Name: "session", Value: "abc123"},
})

resp, _ := client.R().Get("https://api.example.com")
cookies := resp.Cookies()

功能五:文件上传

resp, err := client.R().
    SetFile("avatar", "/path/to/avatar.jpg").
    SetFormData(map[string]string{
        "name": "John",
    }).
    Post("https://api.example.com/upload")

功能六:SSE 支持

client.R().
    SetSSE(true).
    SetSSEHandler(func(event *resty.SSEEvent) {
        fmt.Printf("Event: %s, Data: %s\n", event.Event, event.Data)
    }).
    Get("https://api.example.com/events")

对应场景包括:中间件记录请求日志、超时防止请求挂起、代理适配企业网络、文件上传使用多部分表单、SSE 处理实时数据流。

实战场景

场景一:调用第三方 API

需要调用天气 API,并支持重试和错误处理。

type WeatherResponse struct {
    City        string  `json:"city"`
    Temperature float64 `json:"temperature"`
    Condition   string  `json:"condition"`
}

type WeatherClient struct {
    client *resty.Client
}

func NewWeatherClient() *WeatherClient {
    client := resty.New()

    // 配置重试
    client.SetRetryCount(3).
        SetRetryWaitTime(1 * time.Second).
        AddRetryCondition(func(r *resty.Response, err error) bool {
            return err != nil || r.StatusCode() >= 500
        })

    // 配置超时
    client.SetTimeout(10 * time.Second)

    return &WeatherClient{client: client}
}

func (c *WeatherClient) GetWeather(city string) (*WeatherResponse, error) {
    var resp WeatherResponse

    result, err := c.client.R().
        SetQueryParam("city", city).
        SetResult(&resp).
        Get("https://api.weather.com/v1/weather")
    if err != nil {
        return nil, fmt.Errorf("request failed: %w", err)
    }

    if result.IsError() {
        return nil, fmt.Errorf("API error: %s", result.Status())
    }

    return &resp, nil
}

func main() {
    client := NewWeatherClient()

    weather, err := client.GetWeather("Beijing")
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    fmt.Printf("Weather in %s: %.1f°C, %s\n",
        weather.City, weather.Temperature, weather.Condition)
}

效果:自动重试,错误处理,代码保持简洁。

场景二:微服务间调用

微服务间调用需要负载均衡和熔断。

type ServiceClient struct {
    client *resty.Client
}

func NewServiceClient() *ServiceClient {
    client := resty.New()

    // 配置负载均衡
    client.SetLoadBalancer(&resty.LoadBalancerConfig{
        Strategy: resty.LBStrategyRoundRobin,
        Backends: []string{
            "http://service-1:8080",
            "http://service-2:8080",
            "http://service-3:8080",
        },
    })

    // 配置熔断
    client.SetCircuitBreaker(&resty.CircuitBreakerConfig{
        MaxRequests: 100,
        Interval:    60 * time.Second,
        Timeout:     10 * time.Second,
    })

    // 配置重试
    client.SetRetryCount(2)

    return &ServiceClient{client: client}
}

func (c *ServiceClient) CallService(ctx context.Context, req *Request) (*Response, error) {
    var resp Response

    result, err := c.client.R().
        SetContext(ctx).
        SetBody(req).
        SetResult(&resp).
        Post("/api/process")
    if err != nil {
        return nil, fmt.Errorf("service call failed: %w", err)
    }

    if result.IsError() {
        return nil, fmt.Errorf("service error: %s", result.Status())
    }

    return &resp, nil
}

效果:负载均衡、熔断保护、提高可用性。

场景三:API 测试工具

需要测试 API 并生成 curl 命令。

func TestAPI() {
    client := resty.New()

    req := client.R().
        SetHeader("Authorization", "Bearer token123").
        SetBody(map[string]string{"name": "John"}).
        SetResult(&User{})

    // 生成 curl 命令
    curlCmd := req.GenerateCurlCommand()
    fmt.Println("Curl command:", curlCmd)

    // 执行请求
    resp, err := req.Post("https://api.example.com/users")
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    fmt.Println("Response:", resp.String())
}

效果:生成 curl 命令,方便调试。

这些模式也可以用在爬虫的批量请求和超时控制上。

设计亮点

亮点一:链式调用

流式 API,代码简洁。可读性强,减少临时变量。

亮点二:自动 JSON 处理

自动序列化/反序列化。减少样板代码,类型安全,减少错误。

亮点三:重试机制

内置重试逻辑。提高成功率,应对网络波动,策略可配置。

亮点四:熔断降级

保护系统稳定性。防止雪崩,快速失败,自动恢复。

亮点五:中间件机制

可扩展的请求/响应处理。用于日志记录、认证注入等。

和类似方案对比

vs. net/http

维度go-restynet/http
API链式调用繁琐
JSON自动处理手动
重试内置
熔断内置

结论:go-resty 更易用,net/http 更底层。

vs. req

维度go-restyreq
Star11.8K6K
API链式链式
功能全面精简
性能

结论:go-resty 功能更全,req 更轻量。

vs. heimdall

维度go-restyheimdall
Star11.8K2K
重试内置内置
熔断内置
SSE支持

结论:go-resty 功能更全。

局限性

go-resty 也有局限:

  1. 性能开销:封装带来一定性能损失
  2. 学习成本:API 较多,需要学习
  3. 依赖较多:比标准库复杂
  4. 调试困难:链式调用调试较难
  5. 过度封装:可能隐藏底层细节
  6. 版本兼容:v3 和 v2 有 breaking changes

选择时可以确认是否真的需要高级功能;性能敏感场景可以用 net/http;简单场景可以用更轻量的库;调试时可以用 GenerateCurlCommand

总结

从 go-resty 可以看到几个趋势:

  1. 链式调用是主流:流式 API,代码简洁
  2. 自动处理是刚需:JSON 自动序列化
  3. 重试熔断很重要:提高系统稳定性
  4. 中间件是扩展方式:灵活处理请求/响应
  5. 开发者体验优先:让 HTTP 调用更优雅

如果还在用 net/http 写繁琐的 HTTP 调用,可以试试 go-resty。

参考资料

复制全文 生成海报 Go resty HTTP客户端 net http 熔断降级

推荐文章

程序员茄子在线接单