Client

快速开始

package main

import (
	"context"
	"fmt"
	
	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/app/client"
	"github.com/cloudwego/hertz/pkg/app/server"
	"github.com/cloudwego/hertz/pkg/protocol"
	"github.com/cloudwego/hertz/pkg/protocol/consts"
)

func performRequest() {
	c, _ := client.NewClient()
	req, resp := protocol.AcquireRequest(), protocol.AcquireResponse()
	req.SetRequestURI("http://localhost:8080/hello")

	req.SetMethod("GET")
	_ = c.Do(context.Background(), req, resp)
	fmt.Printf("get response: %s\n", resp.Body())

}

func main() {
	h := server.New(server.WithHostPorts(":8080"))
	h.GET("/hello", func(c context.Context, ctx *app.RequestContext) {
		ctx.JSON(consts.StatusOK, "hello hertz")
	})
	go performRequest()
	h.Spin()
}

配置

配置项 默认值 描述
DialTimeout time.Second 拨号超时时间
MaxConnsPerHost 512 每个主机可能建立的最大连接数
MaxIdleConnDuration 10s 最大的空闲连接持续时间,空闲的保持连接在此持续时间后被关闭
MaxConnDuration 0s 最大的连接持续时间,keep-alive 连接在此持续时间后被关闭。
MaxConnWaitTimeout 0s 等待自由连接的最大时间。
KeepAlive true 是否使用 keep-alive 连接
ClientReadTimeout 0s 完整读取响应(包括body)的最大持续时间。
TLSConfig nil 用来创建一个 tls 连接的 tlsConfig
Dialer network.Dialer 用户自定义拨号器
ResponseBodyStream false 是否在流中读取 body
DisableHeaderNamesNormalizing false 是否禁用头名称规范化
Name "" 用户代理头中使用的客户端名称
NoDefaultUserAgentHeader false 是否没有默认的User-Agent头
DisablePathNormalizing nil 是否禁用路径规范化。
RetryConfig nil 重试配置
WriteTimeout 0s 写入超时时间
ConnStateObserve nil 设置连接状态观察函数
DialFunc network.Dialer 自定义拨号器功能,会覆盖自定义拨号器

WithDialTimeout

WithDialTimeout 函数用于设置拨号超时时间。

函数签名:

func WithDialTimeout(dialTimeout time.Duration) config.ClientOption

示例代码:

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/cloudwego/hertz/pkg/app/client"
)

func main() {
	c, err := client.NewClient(client.WithDialTimeout(1 * time.Second))
	if err != nil {
		return
	}
	status, body, _ := c.Get(context.Background(), nil, "https://www.example.com")
	fmt.Printf("status=%v body=%v\n", status, string(body))
}

WithMaxConnsPerHost

WithMaxConnsPerHost 函数用于设置每个主机最大连接数。

函数签名:

func WithMaxConnsPerHost(maxConns int) config.ClientOption

示例代码:

package main

import (
	"context"
	"fmt"
	"github.com/cloudwego/hertz/pkg/app/client"
)

func main() {
	c, err := client.NewClient(client.WithMaxConnsPerHost(10))
	if err != nil {
		return
	}
	status, body, _ := c.Get(context.Background(), nil, "https://www.example.com")
	fmt.Printf("status=%v body=%v\n", status, string(body))
}

WithMaxIdleConnDuration

WithMaxIdleConnDuration 函数用于设置空闲连接的最大持续时间。如果一条连接在空闲时间超过了设置的最大持续时间,它将被关闭。

函数签名:

func WithMaxIdleConnDuration(maxIdleConnDuration time.Duration) config.ClientOption

示例代码:

package main

import (
    "context"
    "fmt"
    "time"

    "github.com/cloudwego/hertz/pkg/app/client"
)

func main() {
    c, err := client.NewClient(client.WithMaxIdleConnDuration(30 * time.Second))
    if err != nil {
        return
    }
    status, body, _ := c.Get(context.Background(), nil, "https://www.example.com")
    fmt.Printf("status=%v body=%v\n", status, string(body))
}

WithMaxConnDuration

WithMaxConnDuration 函数用于设置连接的最大持续时间。如果一条连接在持续时间超过了设置的最大持续时间,它将被关闭。

函数签名:

func WithMaxConnDuration(maxConnDuration time.Duration) config.ClientOption

示例代码:

package main

import (
    "context"
    "fmt"
    "time"

    "github.com/cloudwego/hertz/pkg/app/client"
)

func main() {
    c, err := client.NewClient(client.WithMaxConnDuration(10 * time.Second))
    if err != nil {
        return
    }
    status, body, _ := c.Get(context.Background(), nil, "https://www.example.com")
    fmt.Printf("status=%v body=%v\n", status, string(body))
}

WithMaxConnWaitTimeout

WithMaxConnWaitTimeout 函数用于设置等待空闲连接的最大持续时间。当 HTTP 客户端需要新的连接时,如果没有可用的空闲连接,它将等待最长时间来获得一个新的连接。如果等待时间超过了设置的最大持续时间,它将返回一个错误。

函数签名:

func WithMaxConnWaitTimeout(t time.Duration) config.ClientOption

示例代码:

package main

import (
    "context"
    "fmt"
    "time"

    "github.com/cloudwego/hertz/pkg/app/client"
)

func main() {
    c, err := client.NewClient(client.WithMaxConnWaitTimeout(5 * time.Second))
    if err != nil {
        return
    }
    status, body, _ := c.Get(context.Background(), nil, "https://www.example.com")
    fmt.Printf("status=%v body=%v\n", status, string(body))
}

WithKeepAlive

WithKeepAlive 函数用于设置 HTTP 客户端是否使用 keep-alive 连接。keep-alive 是一种 HTTP 的持久连接技术,它可以在一次 TCP 连接中处理多个 HTTP 请求和响应,减少了建立和关闭连接的开销,提高了网络性能。

函数签名:

func WithKeepAlive(b bool) config.ClientOption

示例代码:

package main

import (
    "context"
    "fmt"

    "github.com/cloudwego/hertz/pkg/app/client"
)

func main() {
    c, err := client.NewClient(client.WithKeepAlive(true))
    if err != nil {
        return
    }
    status, body, _ := c.Get(context.Background(), nil, "https://www.example.com")
    fmt.Printf("status=%v body=%v\n", status, string(body))
}

WithClientReadTimeout

WithClientReadTimeout 函数用于设置客户端读取完整响应(包括 body)的最大持续时间。它接受一个 time.Duration 类型的参数,表示最大持续时间。如果读取完整响应的时间超过了设置的最大持续时间,客户端将返回一个错误。

函数签名:

func WithClientReadTimeout(t time.Duration) config.ClientOption

示例代码:

package main

import (
    "context"
    "fmt"
    "time"

    "github.com/cloudwego/hertz/pkg/app/client"
)

func main() {
    c, err := client.NewClient(client.WithClientReadTimeout(10 * time.Second))
    if err != nil {
        return
    }
    status, body, _ := c.Get(context.Background(), nil, "https://www.example.com")
    fmt.Printf("status=%v body=%v\n", status, string(body))
}

WithTLSConfig

WithTLSConfig 函数用于设置 TLS 配置,以创建 TLS 连接。TLS(Transport Layer Security)是一种加密协议,用于保护网络通信的安全。

函数签名:

func WithTLSConfig(cfg *tls.Config) config.ClientOption

示例代码:

package main

import (
	"context"
	"crypto/tls"
	"fmt"

	"github.com/cloudwego/hertz/pkg/app/client"
)

func main() {
	tlsConfig := &tls.Config{
		InsecureSkipVerify: true,
	}
	c, err := client.NewClient(client.WithTLSConfig(tlsConfig))
	if err != nil {
		return
	}
	status, body, _ := c.Get(context.Background(), nil, "https://www.example.com")
	fmt.Printf("status=%v body=%v\n", status, string(body))
}

WithDialer

WithDialer 函数用于设置 HTTP 客户端使用的特定 dialer(拨号器)。dialer 用于创建网络连接。

函数签名:

func WithDialer(d network.Dialer) config.ClientOption

示例代码:

package main

import (
	"context"
	"fmt"

	"github.com/cloudwego/hertz/pkg/app/client"
	"github.com/cloudwego/hertz/pkg/network/netpoll"
)

func main() {
	c, err := client.NewClient(client.WithDialer(netpoll.NewDialer()))
	if err != nil {
		return
	}
	status, body, _ := c.Get(context.Background(), nil, "https://www.example.com")
	fmt.Printf("status=%v body=%v\n", status, string(body))
}

WithResponseBodyStream

WithResponseBodyStream 函数用于设置 HTTP 客户端是否以流式方式读取响应体。如果设置为 true,客户端将在读取响应时使用流式读取,以避免一次性将整个响应体加载到内存中。如果设置为 false,客户端将一次性将整个响应体加载到内存中。

函数签名:

func WithResponseBodyStream(b bool) config.ClientOption

示例代码:

package main

import (
    "context"
    "fmt"

    "github.com/cloudwego/hertz/pkg/app/client"
)

func main() {
    c, err := client.NewClient(client.WithResponseBodyStream(true))
    if err != nil {
        return
    }
    status, body, _ := c.Get(context.Background(), nil, "https://www.example.com")
    fmt.Printf("status=%v body=%v\n", status, string(body))
}

WithDisableHeaderNamesNormalizing

WithDisableHeaderNamesNormalizing 函数用于设置 HTTP 客户端是否禁用标准化请求和响应头中的 header 名称。如果设置为 true,客户端将不会标准化请求和响应头中的 header 名称。否则,客户端将标准化请求和响应头中的 header 名称。

函数签名:

func WithDisableHeaderNamesNormalizing(disable bool) config.ClientOption

示例代码:

package main

import (
    "context"
    "fmt"

    "github.com/cloudwego/hertz/pkg/app/client"
)

func main() {
    c, err := client.NewClient(client.WithDisableHeaderNamesNormalizing(true))
    if err != nil {
        return
    }
    status, body, _ := c.Get(context.Background(), nil, "https://www.example.com")
    fmt.Printf("status=%v body=%v\n", status, string(body))
}

WithName

WithName 函数用于设置 HTTP 客户端的名称,该名称将用于 User-Agent 头中。User-Agent 头是 HTTP 请求中的一个标头字段,它向服务器发送有关客户端应用程序、操作系统、版本和其他相关信息的字符串。

函数签名:

func WithName(name string) config.ClientOption

示例代码:

package main

import (
    "context"
    "fmt"

    "github.com/cloudwego/hertz/pkg/app/client"
)

func main() {
    c, err := client.NewClient(client.WithName("my-client"))
    if err != nil {
        return
    }
    status, body, _ := c.Get(context.Background(), nil, "https://www.example.com")
    fmt.Printf("status=%v body=%v\n", status, string(body))
}

WithNoDefaultUserAgentHeader

WithNoDefaultUserAgentHeader 函数用于设置 HTTP 客户端是否禁用默认的 User-Agent 头。如果设置为 true,客户端将不会在请求中发送默认的 User-Agent 头。否则,客户端将发送默认的 User-Agent 头。

函数签名:

func WithNoDefaultUserAgentHeader(isNoDefaultUserAgentHeader bool) config.ClientOption

示例代码:

package main

import (
    "context"
    "fmt"

    "github.com/cloudwego/hertz/pkg/app/client"
)

func main() {
    c, err := client.NewClient(client.WithNoDefaultUserAgentHeader(true))
    if err != nil {
        return
    }
    status, body, _ := c.Get(context.Background(), nil, "https://www.example.com")
    fmt.Printf("status=%v body=%v\n", status, string(body))
}

WithDisablePathNormalizing

WithDisablePathNormalizing 函数用于设置 HTTP 客户端是否禁用标准化请求路径。如果设置为 true,客户端将不会标准化请求路径。否则,客户端将标准化请求路径。

函数签名:

func WithDisablePathNormalizing(isDisablePathNormalizing bool) config.ClientOption

示例代码:

package main

import (
    "context"
    "fmt"

    "github.com/cloudwego/hertz/pkg/app/client"
)

func main() {
    c, err := client.NewClient(client.WithDisablePathNormalizing(true))
    if err != nil {
        return
    }
    status, body, _ := c.Get(context.Background(), nil, "https://www.example.com/path/../path/./subpath")
    fmt.Printf("status=%v body=%v\n", status, string(body))
}

WithRetryConfig

WithRetryConfig 函数用于设置 HTTP 客户端的重试配置。在发生网络故障或超时等问题时,客户端可以通过重试来尝试重新建立连接或重新发送请求。

函数签名:

func WithRetryConfig(opts ...retry.Option) config.ClientOption

示例代码:

package main

import (
	"context"
	"fmt"
	"github.com/cloudwego/hertz/pkg/app/client"
	"github.com/cloudwego/hertz/pkg/app/client/retry"
)

func main() {
	c, err := client.NewClient(client.WithRetryConfig(
		retry.WithMaxAttemptTimes(3),
		retry.WithInitDelay(1000),
		retry.WithMaxDelay(10000),
		retry.WithDelayPolicy(retry.DefaultDelayPolicy),
		retry.WithMaxJitter(1000),
	))
	if err != nil {
		return
	}
	status, body, _ := c.Get(context.Background(), nil, "https://www.example.com")
	fmt.Printf("status=%v body=%v\n", status, string(body))
}

WithWriteTimeout

WithWriteTimeout 函数用于设置 HTTP 客户端的写入超时时间。在发送请求时,如果写入请求的数据超过了指定的超时时间,则客户端会中止请求并返回错误。

函数签名:

func WithWriteTimeout(t time.Duration) config.ClientOption

示例代码:

package main

import (
    "context"
    "fmt"
    "time"

    "github.com/cloudwego/hertz/pkg/app/client"
)

func main() {
    c, err := client.NewClient(client.WithWriteTimeout(1*time.Second))
    if err != nil {
        return
    }
    status, body, _ := c.Post(context.Background(), nil, "https://www.example.com", []byte("hello, world!"))
    fmt.Printf("status=%v body=%v\n", status, string(body))
}

WithConnStateObserve

WithConnStateObserve 函数用于设置 HTTP 客户端的连接状态观察函数。在客户端建立连接、关闭连接或发生其他连接状态变化时,该观察函数会被调用。

函数签名:

func WithConnStateObserve(hs config.HostClientStateFunc, interval ...time.Duration) config.ClientOption

示例代码:

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/cloudwego/hertz/pkg/app/client"
	"github.com/cloudwego/hertz/pkg/common/config"
)

func main() {
	observeInterval := 10 * time.Second
	stateFunc := func(state config.HostClientState) {
		fmt.Printf("state=%v\n", state)
	}
	c, err := client.NewClient(client.WithConnStateObserve(stateFunc, observeInterval))
	if err != nil {
		return
	}
  
	status, body, _ := c.Get(context.Background(), nil, "https://www.example.com")
	fmt.Printf("status=%v body=%v\n", status, string(body))
}

WithDialFunc

WithDialFunc 函数用于设置 HTTP 客户端的拨号函数,即客户端用于建立连接的底层网络拨号器。该函数接受一个 network.DialFunc 类型的参数,表示客户端使用的自定义拨号函数。

函数签名:

func WithDialFunc(f network.DialFunc, dialers ...network.Dialer) config.ClientOption

示例代码:

package main

import (
	"context"
	"fmt"
	
	"github.com/cloudwego/hertz/pkg/app/client"
	"github.com/cloudwego/hertz/pkg/network"
	"github.com/cloudwego/hertz/pkg/network/netpoll"
)

func main() {
	var customDialFunc network.DialFunc = func(addr string) (network.Conn, error) {
		return nil, nil
	}
	c, err := client.NewClient(client.WithDialFunc(customDialFunc, netpoll.NewDialer()))
	if err != nil {
		return
	}
	status, body, _ := c.Get(context.Background(), nil, "https://www.example.com")
	fmt.Printf("status=%v body=%v\n", status, string(body))
}

Do

Do 函数执行给定的 http 请求并填充给定的 http 响应。请求必须包含至少一个非零的RequestURI,其中包含完整的URL或非零的 Host header + RequestURI。

该函数不会跟随重定向。请使用 Ge t函数来跟随重定向。

如果resp为nil,则会忽略响应。如果所有针对请求主机的DefaultMaxConnsPerHost连接都已忙,则会返回ErrNoFreeConns错误。在性能关键的代码中,建议通过 AcquireRequest 和 AcquireResponse 获取 req 和resp。

函数签名:

func (c *Client) Do(ctx context.Context, req *protocol.Request, resp *protocol.Response) error 

示例代码:

package main

import (
	"context"
	"fmt"

	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/app/client"
	"github.com/cloudwego/hertz/pkg/app/server"
	"github.com/cloudwego/hertz/pkg/protocol"
	"github.com/cloudwego/hertz/pkg/protocol/consts"
)

func performRequest() {

	c, err := client.NewClient()
	if err != nil {
		return
	}

	req, res := &protocol.Request{}, &protocol.Response{}
	req.SetMethod(consts.MethodGet)
	req.SetRequestURI("http://localhost:8080/ping")

	err = c.Do(context.Background(), req, res)
	fmt.Printf("resp = %v,err = %+v", string(res.Body()), err)

}

func main() {
	h := server.New(server.WithHostPorts(":8080"))
	h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
		ctx.String(consts.StatusOK, "pong")
	})

	go performRequest()

	h.Spin()
}

DoTimeout

DoTimeout函数执行给定的请求并在给定的超时时间内等待响应。

如果 resp 为 nil,则会忽略响应。如果在给定的超时时间内未能收到响应,则会返回 errTimeout 错误。

函数签名:

func DoTimeout(ctx context.Context, req *protocol.Request, resp *protocol.Response, timeout time.Duration) error

示例代码:

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/app/client"
	"github.com/cloudwego/hertz/pkg/app/server"
	"github.com/cloudwego/hertz/pkg/protocol"
	"github.com/cloudwego/hertz/pkg/protocol/consts"
)

func performRequest() {

	c, err := client.NewClient()
	if err != nil {
		return
	}

	req, res := &protocol.Request{}, &protocol.Response{}
	req.SetMethod(consts.MethodGet)
	req.SetRequestURI("http://localhost:8080/ping")

	err = c.DoTimeout(context.Background(), req, res, time.Second*3)
	fmt.Printf("resp = %v,err = %+v\n", string(res.Body()), err)
	err = c.DoTimeout(context.Background(), req, res, time.Second)
	fmt.Printf("resp = %v,err = %+v\n", string(res.Body()), err)

}

func main() {
	h := server.New(server.WithHostPorts(":8080"))
	h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
		time.Sleep(2 * time.Second)
		ctx.String(consts.StatusOK, "pong")
	})

	go performRequest()

	h.Spin()
}

DoDeadline

DoDeadline 执行给定的请求并等待响应,直至给定的最后期限。 如果resp为 nil,则会忽略响应。如果在给定的截止日期之前未能收到响应,则会返回 errTimeout 错误。

函数签名:

func DoDeadline(ctx context.Context, req *protocol.Request, resp *protocol.Response, deadline time.Time) error

示例代码:

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/app/client"
	"github.com/cloudwego/hertz/pkg/app/server"
	"github.com/cloudwego/hertz/pkg/protocol"
	"github.com/cloudwego/hertz/pkg/protocol/consts"
)

func performRequest() {

	c, err := client.NewClient()
	if err != nil {
		return
	}

	req, res := &protocol.Request{}, &protocol.Response{}
	req.SetMethod(consts.MethodGet)
	req.SetRequestURI("http://localhost:8080/ping")

	err = c.DoDeadline(context.Background(), req, res, time.Now().Add(3*time.Second))
	fmt.Printf("resp = %v,err = %+v\n", string(res.Body()), err)
	err = c.DoDeadline(context.Background(), req, res, time.Now().Add(1*time.Second))
	fmt.Printf("resp = %v,err = %+v\n", string(res.Body()), err)

}

func main() {
	h := server.New(server.WithHostPorts(":8080"))
	h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
		time.Sleep(2 * time.Second)
		ctx.String(consts.StatusOK, "pong")
	})

	go performRequest()

	h.Spin()
}

DoRedirects

DoRedirects 函数执行给定的 http 请求并填充给定的 http 响应,遵循最多 maxRedirectsCount 次重定向。当重定向次数超过maxRedirectsCount 时,将返回 ErrTooManyRedirects 错误。

函数签名:

func DoRedirects(ctx context.Context, req *protocol.Request, resp *protocol.Response, maxRedirectsCount int) error

示例代码:

package main

import (
	"context"
	"fmt"

	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/app/client"
	"github.com/cloudwego/hertz/pkg/app/server"
	"github.com/cloudwego/hertz/pkg/protocol"
	"github.com/cloudwego/hertz/pkg/protocol/consts"
)

func performRequest() {
	c, err := client.NewClient()
	if err != nil {
		return
	}

	req, res := &protocol.Request{}, &protocol.Response{}
	req.SetMethod(consts.MethodGet)
	req.SetRequestURI("http://localhost:8080/redirect")

	err = c.DoRedirects(context.Background(), req, res, 1)
	fmt.Printf("resp = %v,err = %+v\n", string(res.Body()), err)

	err = c.DoRedirects(context.Background(), req, res, 2)
	fmt.Printf("resp = %v,err = %+v\n", string(res.Body()), err)
}

func main() {
	h := server.New(server.WithHostPorts(":8080"))
	h.GET("/redirect", func(c context.Context, ctx *app.RequestContext) {
		ctx.Redirect(consts.StatusMovedPermanently, []byte("/redirect2"))
	})
	h.GET("/redirect2", func(c context.Context, ctx *app.RequestContext) {
		ctx.Redirect(consts.StatusMovedPermanently, []byte("/redirect3"))
	})
	h.GET("/redirect3", func(c context.Context, ctx *app.RequestContext) {
		ctx.String(consts.StatusOK, "pong")
	})

	go performRequest()

	h.Spin()
}

Get

Get 函数返回 URL 的状态码和响应体。如果 dst 太小,则将被响应体替换并返回,否则将分配一个新的切片。

该函数会自动跟随重定向。如果需要手动处理重定向,请使用 Do 函数。

函数签名:

func Get(ctx context.Context, dst []byte, url string, requestOptions ...config.RequestOption) (statusCode int, body []byte, err error)

示例代码:

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/app/client"
	"github.com/cloudwego/hertz/pkg/app/server"
	"github.com/cloudwego/hertz/pkg/protocol/consts"
)

func performRequest() {
	c, err := client.NewClient()
	if err != nil {
		return
	}
	status, body, err := c.Get(context.Background(), nil, "http://localhost:8080/ping")
	fmt.Printf("status=%v body=%v err=%v\n", status, string(body), err)
}

func main() {
	h := server.New(server.WithHostPorts(":8080"))
	h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
		ctx.String(consts.StatusOK, "pong")
	})
	go performRequest()

	h.Spin()
}

GetTimeOut

GetTimeout函数返回URL的状态码和响应体。如果dst太小,则将被响应体替换并返回,否则将分配一个新的切片。该函数会自动跟随重定向。如果需要手动处理重定向,请使用Do函数。

如果在给定的超时时间内无法获取URL的内容,则会返回errTimeout错误。

警告:GetTimeout不会终止请求本身。该请求将在后台继续进行,并且响应将被丢弃。如果请求时间过长且连接池已满,请尝试使用具有ReadTimeout配置的自定义Client实例或像下面这样设置请求级别的读取超时时间:

 codeGetTimeout(ctx, dst, url, timeout, config.WithReadTimeout(1 * time.Second)) 

函数签名:

func GetTimeout(ctx context.Context, dst []byte, url string, timeout time.Duration, requestOptions ...config.RequestOption) (statusCode int, body []byte, err error)

示例代码:

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/app/client"
	"github.com/cloudwego/hertz/pkg/app/server"
	"github.com/cloudwego/hertz/pkg/protocol/consts"
)

func performRequest() {
	c, err := client.NewClient()
	if err != nil {
		return
	}
	status, body, err := c.GetTimeout(context.Background(), nil, "http://localhost:8080/ping", 3*time.Second)
	fmt.Printf("status=%v body=%v err=%v\n", status, string(body), err)

	status, body, err = c.GetTimeout(context.Background(), nil, "http://localhost:8080/ping", 1*time.Second)
	fmt.Printf("status=%v body=%v err=%v\n", status, string(body), err)
}

func main() {
	h := server.New(server.WithHostPorts(":8080"))
	h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
		time.Sleep(2 * time.Second)
		ctx.String(consts.StatusOK, "pong")
	})
	go performRequest()

	h.Spin()
}

GetDeadline

GetDeadline函数返回URL的状态码和响应体。如果dst太小,则将被响应体替换并返回,否则将分配一个新的切片。该函数会自动跟随重定向。如果需要手动处理重定向,请使用Do函数。

如果在给定的截止时间之前无法获取URL的内容,则会返回errTimeout错误。

警告:GetDeadline不会终止请求本身。该请求将在后台继续进行,并且响应将被丢弃。如果请求时间过长且连接池已满,请尝试使用具有ReadTimeout配置的自定义Client实例或像下面这样设置请求级别的读取超时时间:

GetDeadline(ctx, dst, url, deadline, config.WithReadTimeout(1 * time.Second))

函数签名:

func GetDeadline(ctx context.Context, dst []byte, url string, deadline time.Time, requestOptions ...config.RequestOption) (statusCode int, body []byte, err error)

示例代码:

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/app/client"
	"github.com/cloudwego/hertz/pkg/app/server"
	"github.com/cloudwego/hertz/pkg/protocol/consts"
)

func performRequest() {
	c, err := client.NewClient()
	if err != nil {
		return
	}
	status, body, err := c.GetDeadline(context.Background(), nil, "http://localhost:8080/ping", time.Now().Add(3*time.Second))
	fmt.Printf("status=%v body=%v err=%v\n", status, string(body), err)

	status, body, err = c.GetDeadline(context.Background(), nil, "http://localhost:8080/ping", time.Now().Add(time.Second))
	fmt.Printf("status=%v body=%v err=%v\n", status, string(body), err)
}

func main() {
	h := server.New(server.WithHostPorts(":8080"))
	h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
		time.Sleep(2 * time.Second)
		ctx.String(consts.StatusOK, "pong")
	})
	go performRequest()

	h.Spin()
}

Post

Post 函数使用给定的 POST 参数向指定的 UR L发送 POST 请求。如果 dst 太小,则将被响应体替换并返回,否则将分配一个新的切片。该函数会自动跟随重定向。如果需要手动处理重定向,请使用 Do 函数。

如果 postArgs 为 nil ,则发送空的 POST 请求体。

函数签名:

func Post(ctx context.Context, dst []byte, url string, postArgs *protocol.Args, requestOptions ...config.RequestOption) (statusCode int, body []byte, err error)

示例代码:

package main

import (
	"context"
	"fmt"

	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/app/client"
	"github.com/cloudwego/hertz/pkg/app/server"
	"github.com/cloudwego/hertz/pkg/protocol"
	"github.com/cloudwego/hertz/pkg/protocol/consts"
)

func performRequest() {
	c, err := client.NewClient()
	if err != nil {
		return
	}
  
	var postArgs protocol.Args
	postArgs.Set("name", "cloudwego") // Set post args
	status, body, err := c.Post(context.Background(), nil, "http://localhost:8080/hello", &postArgs)
	fmt.Printf("status=%v body=%v err=%v\n", status, string(body), err)
}

func main() {
	h := server.New(server.WithHostPorts(":8080"))
	h.POST("/hello", func(c context.Context, ctx *app.RequestContext) {
		ctx.String(consts.StatusOK, "hello %s", ctx.PostForm("name"))
	})
	go performRequest()

	h.Spin()
}

SetProxy

SetProxy 用来设置客户端代理的。

注意,同一个客户端不能设置多个代理,如果需要使用另一个代理,请创建另一个客户端并为其设置代理。

示例代码:

func (c *Client) SetProxy(p protocol.Proxy)

函数签名:

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/app/client"
	"github.com/cloudwego/hertz/pkg/app/server"
	"github.com/cloudwego/hertz/pkg/network/standard"
	"github.com/cloudwego/hertz/pkg/protocol"
	"github.com/cloudwego/hertz/pkg/protocol/consts"
)

func performRequest() {

	client, err := client.NewClient(client.WithDialer(standard.NewDialer()))
	if err != nil {
		log.Print(err)
		return
	}
	client.SetProxy(protocol.ProxyURI(protocol.ParseURI("http://localhost:8080")))
	status, body, err := client.Get(context.Background(), nil, "http://localhost:8081/ping")

	fmt.Printf("status=%v body=%v err=%v\n", status, string(body), err)
}

func main() {

	h1 := server.New(server.WithHostPorts(":8080"))

	h1.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
		cli, err := client.NewClient()
		if err != nil {
			log.Printf("client.NewClient err: %v", err)
		}

		req, res := &protocol.Request{}, &protocol.Response{}
		req.SetMethod(consts.MethodGet)
		req.SetRequestURI("http://localhost:8081/ping")

		cli.Do(context.Background(), req, res)
		ctx.String(res.StatusCode(), res.BodyBuffer().String())

	})
	go h1.Spin()

	h2 := server.New(server.WithHostPorts(":8081"))
	h2.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
		ctx.String(consts.StatusOK, "pong")
	})
	go h2.Spin()

	performRequest()

}

SetRetryIfFunc

SetRetryIfFunc 方法用于设置客户端的重试函数,该函数用于判断在请求失败时是否应该重试。

函数签名:

func (c *Client) SetRetryIfFunc(retryIf client.RetryIfFunc)

示例代码:

package main

import (
	"context"
	"fmt"
	"github.com/cloudwego/hertz/pkg/app/client"
	"github.com/cloudwego/hertz/pkg/protocol"
)

func main() {

	c, err := client.NewClient()
	if err != nil {
		return
	}
	status, body, _ := c.Get(context.Background(), nil, "https://www.example.com")
	fmt.Printf("status=%v body=%v\n", status, string(body))

	var customRetryIfFunc = func(req *protocol.Request, resp *protocol.Response, err error) bool {
		return true
	}

	c.SetRetryIfFunc(customRetryIfFunc)

	status2, body2, _ := c.Get(context.Background(), nil, "https://www.example.com")
	fmt.Printf("status=%v body=%v\n", status2, string(body2))
}

SetClientFactory

SetClientFactory 方法用于设置客户端工厂对象,该工厂对象用于创建 HTTP 客户端对象。

函数签名:

func (c *Client) SetClientFactory(cf suite.ClientFactory)

示例代码:

package main

import (
	"context"
	"fmt"
	
	"github.com/cloudwego/hertz/pkg/app/client"
	"github.com/cloudwego/hertz/pkg/protocol/http1/factory"
)

func main() {

	c, err := client.NewClient()
	if err != nil {
		return
	}

	c.SetClientFactory(factory.NewClientFactory(nil))

	status, body, _ := c.Get(context.Background(), nil, "https://www.example.com")
	fmt.Printf("status=%v body=%v\n", status, string(body))
}

CloseIdleConnections

CloseIdleConnections 方法用于关闭任何处于空闲状态的 “keep-alive” 连接。这些连接可能是之前的请求所建立的,但现在已经空闲了一段时间。该方法不会中断任何当前正在使用的连接。

函数签名:

func (c *Client) CloseIdleConnections()

示例代码:

package main

import (
    "context"
    "fmt"
    "github.com/cloudwego/hertz/pkg/app/client"
)

func main() {

    c, err := client.NewClient()
    if err != nil {
        return
    }
    status, body, _ := c.Get(context.Background(), nil, "https://www.example.com")
    fmt.Printf("status=%v body=%v\n", status, string(body))

    // 关闭空闲连接
    c.CloseIdleConnections()
}

GetDialerName

GetDialerName 方法用于获取客户端当前使用的拨号器的名称。如果无法获取拨号器名称,则返回 “unknown”。

函数签名:

func (c *Client) GetDialerName() (dName string, err error)

示例代码:

package main

import (
    "context"
    "fmt"
    "github.com/cloudwego/hertz/pkg/app/client"
)

func main() {

    c, err := client.NewClient()
    if err != nil {
        return
    }
    status, body, _ := c.Get(context.Background(), nil, "https://www.example.com")
    fmt.Printf("status=%v body=%v\n", status, string(body))

    // 获取拨号器名称
    dName, err := c.GetDialerName()
    if err != nil {
        fmt.Printf("GetDialerName failed: %v", err)
        return
    }
    fmt.Printf("dialer name=%v\n", dName)
}

GetOptions

GetOptions 函数返回 Client 实例的 ClientOptions 结构体指针。

函数签名:

func (c *Client) GetOptions() *config.ClientOptions

示例代码:

package main

import (
	"fmt"

	"github.com/cloudwego/hertz/pkg/app/client"
)

func main() {
	c, err := client.NewClient()
	if err != nil {
		return
	}

	options := c.GetOptions()
	fmt.Println(options.DialTimeout)
}

Middleware

使用 Use 方法对当前 client 增加一个中间件。

函数签名:

type Middleware func(Endpoint) Endpoint

示例代码:

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/app/client"
	"github.com/cloudwego/hertz/pkg/app/server"
	"github.com/cloudwego/hertz/pkg/protocol"
	"github.com/cloudwego/hertz/pkg/protocol/consts"
)

func MyMiddleware(next client.Endpoint) client.Endpoint {
	return func(ctx context.Context, req *protocol.Request, resp *protocol.Response) (err error) {
		// pre-handle
		fmt.Println("pre-clientHandle")
		if err = next(ctx, req, resp); err != nil {
			fmt.Println(err)
			return err
		}
		// post-handle
		fmt.Println("post-clientHandle")
		return nil
	}
}

func performRequest() {

	time.Sleep(1 * time.Second)
	client, err := client.NewClient()
	if err != nil {
		return
	}

	client.Use(MyMiddleware)
	req, res := &protocol.Request{}, &protocol.Response{}
	req.SetRequestURI("http://127.0.0.1:8080/ping")

	err = client.Do(context.Background(), req, res)
	fmt.Printf("resp = %v,err = %+v", string(res.Body()), err)
}

func main() {
	h := server.New(server.WithHostPorts(":8080"))
	h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
		ctx.String(consts.StatusOK, "pong")
	})

	go performRequest()

	h.Spin()
}

最后修改 June 27, 2023 : feat: add client doc (fbf32e6)