123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- package htfutures
- import (
- "context"
- "encoding/json"
- "errors"
- "eta/eta_bridge/global"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "strings"
- "time"
- )
- const (
- get = "GET"
- post = "POST"
- )
- type HttpClient[T any] interface {
- Get(url string, head http.Header, params map[string]interface{}) (Response[T], error)
- Post(url string, head http.Header, data interface{}) (Response[T], error)
- PostFromData(url string, fromData url.Values) (Response[T], error)
- doRequest(req *http.Request) (Response[T], error)
- }
- type Response[T any] struct {
- Code int `json:"code"`
- Message string `json:"message"`
- Data T `json:"data"`
- TraceId string `json:"traceId"`
- }
- // DefaultHttpClient 结构体实现 SSOClient 接口
- type DefaultHttpClient[T any] struct {
- client *http.Client
- retryStrategy RetryStrategy
- }
- func (c *DefaultHttpClient[T]) PostFromData(urlStr string, fromData url.Values) (Response[T], error) {
- dataStr, err := json.Marshal(fromData)
- if err != nil {
- global.FILE_LOG.Error("formData参数json序列化失败,err:" + err.Error())
- }
- global.FILE_LOG.Info("post请求:" + urlStr + ",formData:" + string(dataStr))
- return c.doPostForm(urlStr, fromData)
- }
- func (c *DefaultHttpClient[T]) Post(urlStr string, head http.Header, data interface{}) (response Response[T], err error) {
- dataStr, err := json.Marshal(data)
- if err != nil {
- global.FILE_LOG.Error("请求data json序列化失败,err:" + err.Error())
- }
- global.FILE_LOG.Info("post请求:" + urlStr + ",data:" + string(dataStr))
- body := io.NopCloser(strings.NewReader(string(dataStr)))
- req, err := http.NewRequest(post, urlStr, body)
- if err != nil {
- global.FILE_LOG.Error("创建http求求失败:" + err.Error())
- return
- }
- if head != nil {
- req.Header = head
- var headStr []byte
- headStr, err = json.Marshal(head)
- if err != nil {
- global.FILE_LOG.Error("请求头json序列化失败,err:" + err.Error())
- }
- global.FILE_LOG.Info("自定义请求头:" + string(headStr))
- }
- return c.doRequest(req)
- }
- func (c *DefaultHttpClient[T]) Get(urlStr string, head http.Header, params map[string]interface{}) (response Response[T], err error) {
- if len(params) > 0 {
- queryParams := url.Values{}
- for key, value := range params {
- queryParams.Add(key, fmt.Sprintf("%v", value))
- }
- urlStr = urlStr + "?" + queryParams.Encode()
- global.FILE_LOG.Info("带参get请求:" + urlStr)
- }
- req, err := http.NewRequest(get, urlStr, nil)
- if err != nil {
- global.FILE_LOG.Error("创建http求求失败:" + err.Error())
- return
- }
- if head != nil {
- req.Header = head
- headStr, _ := json.Marshal(head)
- global.FILE_LOG.Info("自定义请求头:" + string(headStr))
- }
- return c.doRequest(req)
- }
- func (c *DefaultHttpClient[T]) RetryStrategy(retryStrategy RetryStrategy) *DefaultHttpClient[T] {
- c.retryStrategy = retryStrategy
- return c
- }
- // retryRequest 尝试执行请求并根据重试策略重试
- // retryRequest 尝试执行请求并根据重试策略重试
- func (c *DefaultHttpClient[T]) retryRequest(requestFunc func() (*http.Response, error)) (result *http.Response, err error) {
- for i := 0; ; i++ {
- result, err = requestFunc()
- if err == nil || !c.retryStrategy.ShouldRetry(i, err) {
- return
- }
- global.FILE_LOG.Error("http请求失败,开始第" + string(rune(i+1)) + "次重试,最近一次错误:[" + err.Error() + "]")
- time.Sleep(c.retryStrategy.RetryDelay(i))
- }
- }
- func (c *DefaultHttpClient[T]) doPostForm(urlStr string, values url.Values) (response Response[T], err error) {
- global.FILE_LOG.Info("发送请求:" + urlStr)
- //ctx 用Background
- resp, err := c.retryRequest(func() (*http.Response, error) {
- return c.client.PostForm(urlStr, values)
- })
- if err != nil {
- global.FILE_LOG.Error("http请求失败:" + err.Error())
- return
- }
- defer resp.Body.Close()
- repBody, err := io.ReadAll(resp.Body)
- if err != nil {
- global.FILE_LOG.Error("http请求失败,读取body失败:" + err.Error())
- return
- }
- if resp.StatusCode != http.StatusOK {
- global.FILE_LOG.Error("http请求异常,请求状态为:" + resp.Status)
- }
- global.FILE_LOG.Info("http应答为:" + string(repBody))
- err = json.Unmarshal(repBody, &response)
- if err != nil {
- global.FILE_LOG.Error("解析应答失败,反序列化失败:" + err.Error())
- }
- return
- }
- // DoRequest 执行 HTTP 请求
- func (c *DefaultHttpClient[T]) doRequest(req *http.Request) (response Response[T], err error) {
- global.FILE_LOG.Info("发送请求:")
- //ctx 用Background
- resp, err := c.retryRequest(func() (*http.Response, error) {
- return c.client.Do(req)
- })
- if err != nil {
- global.FILE_LOG.Error("http请求失败:" + err.Error())
- return
- }
- defer resp.Body.Close()
- repBody, err := io.ReadAll(resp.Body)
- if err != nil {
- global.FILE_LOG.Error("http请求失败,读取body失败:" + err.Error())
- return
- }
- if resp.StatusCode != http.StatusOK {
- global.FILE_LOG.Error("http请求异常,请求状态为:" + resp.Status)
- return
- }
- global.FILE_LOG.Info("htt请求成功,body:" + string(repBody))
- return
- }
- // RetryStrategy 接口定义
- type RetryStrategy interface {
- ShouldRetry(attempt int, err error) bool
- RetryDelay(attempt int) time.Duration
- }
- // FixedRetryStrategy 实现 RetryStrategy 接口
- type FixedRetryStrategy struct {
- maxAttempts int
- delay time.Duration
- }
- // NewFixedRetryStrategy 创建 FixedRetryStrategy 实例
- func newFixedRetryStrategy(maxAttempts int, delay time.Duration) *FixedRetryStrategy {
- return &FixedRetryStrategy{
- maxAttempts: maxAttempts,
- delay: delay,
- }
- }
- // ShouldRetry 判断是否应该重试
- func (s *FixedRetryStrategy) ShouldRetry(attempt int, err error) bool {
- return attempt < s.maxAttempts && retryErr(err)
- }
- func (s *FixedRetryStrategy) Header() {
- }
- func retryErr(err error) bool {
- return errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled)
- }
- // RetryDelay 返回重试延迟时间
- func (s *FixedRetryStrategy) RetryDelay(attempt int) time.Duration {
- return time.Duration(attempt) * s.delay
- }
- func CreateDefault[T any]() *DefaultHttpClient[T] {
- return &DefaultHttpClient[T]{
- client: &http.Client{Timeout: 10 * time.Second},
- retryStrategy: newFixedRetryStrategy(3, 3*time.Second),
- }
- }
|