http_client.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. package htfutures
  2. import (
  3. "context"
  4. "encoding/json"
  5. "errors"
  6. "eta/eta_bridge/global"
  7. "fmt"
  8. "io"
  9. "net/http"
  10. "net/url"
  11. "strings"
  12. "time"
  13. )
  14. const (
  15. get = "GET"
  16. post = "POST"
  17. )
  18. type HttpClient[T any] interface {
  19. Get(url string, head http.Header, params map[string]interface{}) (Response[T], error)
  20. Post(url string, head http.Header, data interface{}) (Response[T], error)
  21. PostFromData(url string, fromData url.Values) (Response[T], error)
  22. doRequest(req *http.Request) (Response[T], error)
  23. }
  24. type Response[T any] struct {
  25. Code int `json:"code"`
  26. Message string `json:"message"`
  27. Data T `json:"data"`
  28. TraceId string `json:"traceId"`
  29. }
  30. // DefaultHttpClient 结构体实现 SSOClient 接口
  31. type DefaultHttpClient[T any] struct {
  32. client *http.Client
  33. retryStrategy RetryStrategy
  34. }
  35. func (c *DefaultHttpClient[T]) PostFromData(urlStr string, fromData url.Values) (Response[T], error) {
  36. dataStr, err := json.Marshal(fromData)
  37. if err != nil {
  38. global.FILE_LOG.Error("formData参数json序列化失败,err:" + err.Error())
  39. }
  40. global.FILE_LOG.Info("post请求:" + urlStr + ",formData:" + string(dataStr))
  41. return c.doPostForm(urlStr, fromData)
  42. }
  43. func (c *DefaultHttpClient[T]) Post(urlStr string, head http.Header, data interface{}) (response Response[T], err error) {
  44. dataStr, err := json.Marshal(data)
  45. if err != nil {
  46. global.FILE_LOG.Error("请求data json序列化失败,err:" + err.Error())
  47. }
  48. global.FILE_LOG.Info("post请求:" + urlStr + ",data:" + string(dataStr))
  49. body := io.NopCloser(strings.NewReader(string(dataStr)))
  50. req, err := http.NewRequest(post, urlStr, body)
  51. if err != nil {
  52. global.FILE_LOG.Error("创建http求求失败:" + err.Error())
  53. return
  54. }
  55. if head != nil {
  56. req.Header = head
  57. var headStr []byte
  58. headStr, err = json.Marshal(head)
  59. if err != nil {
  60. global.FILE_LOG.Error("请求头json序列化失败,err:" + err.Error())
  61. }
  62. global.FILE_LOG.Info("自定义请求头:" + string(headStr))
  63. }
  64. return c.doRequest(req)
  65. }
  66. func (c *DefaultHttpClient[T]) Get(urlStr string, head http.Header, params map[string]interface{}) (response Response[T], err error) {
  67. if len(params) > 0 {
  68. queryParams := url.Values{}
  69. for key, value := range params {
  70. queryParams.Add(key, fmt.Sprintf("%v", value))
  71. }
  72. urlStr = urlStr + "?" + queryParams.Encode()
  73. global.FILE_LOG.Info("带参get请求:" + urlStr)
  74. }
  75. req, err := http.NewRequest(get, urlStr, nil)
  76. if err != nil {
  77. global.FILE_LOG.Error("创建http求求失败:" + err.Error())
  78. return
  79. }
  80. if head != nil {
  81. req.Header = head
  82. headStr, _ := json.Marshal(head)
  83. global.FILE_LOG.Info("自定义请求头:" + string(headStr))
  84. }
  85. return c.doRequest(req)
  86. }
  87. func (c *DefaultHttpClient[T]) RetryStrategy(retryStrategy RetryStrategy) *DefaultHttpClient[T] {
  88. c.retryStrategy = retryStrategy
  89. return c
  90. }
  91. // retryRequest 尝试执行请求并根据重试策略重试
  92. // retryRequest 尝试执行请求并根据重试策略重试
  93. func (c *DefaultHttpClient[T]) retryRequest(requestFunc func() (*http.Response, error)) (result *http.Response, err error) {
  94. for i := 0; ; i++ {
  95. result, err = requestFunc()
  96. if err == nil || !c.retryStrategy.ShouldRetry(i, err) {
  97. return
  98. }
  99. global.FILE_LOG.Error("http请求失败,开始第" + string(rune(i+1)) + "次重试,最近一次错误:[" + err.Error() + "]")
  100. time.Sleep(c.retryStrategy.RetryDelay(i))
  101. }
  102. }
  103. func (c *DefaultHttpClient[T]) doPostForm(urlStr string, values url.Values) (response Response[T], err error) {
  104. global.FILE_LOG.Info("发送请求:" + urlStr)
  105. //ctx 用Background
  106. resp, err := c.retryRequest(func() (*http.Response, error) {
  107. return c.client.PostForm(urlStr, values)
  108. })
  109. if err != nil {
  110. global.FILE_LOG.Error("http请求失败:" + err.Error())
  111. return
  112. }
  113. defer resp.Body.Close()
  114. repBody, err := io.ReadAll(resp.Body)
  115. if err != nil {
  116. global.FILE_LOG.Error("http请求失败,读取body失败:" + err.Error())
  117. return
  118. }
  119. if resp.StatusCode != http.StatusOK {
  120. global.FILE_LOG.Error("http请求异常,请求状态为:" + resp.Status)
  121. }
  122. global.FILE_LOG.Info("http应答为:" + string(repBody))
  123. err = json.Unmarshal(repBody, &response)
  124. if err != nil {
  125. global.FILE_LOG.Error("解析应答失败,反序列化失败:" + err.Error())
  126. }
  127. return
  128. }
  129. // DoRequest 执行 HTTP 请求
  130. func (c *DefaultHttpClient[T]) doRequest(req *http.Request) (response Response[T], err error) {
  131. global.FILE_LOG.Info("发送请求:")
  132. //ctx 用Background
  133. resp, err := c.retryRequest(func() (*http.Response, error) {
  134. return c.client.Do(req)
  135. })
  136. if err != nil {
  137. global.FILE_LOG.Error("http请求失败:" + err.Error())
  138. return
  139. }
  140. defer resp.Body.Close()
  141. repBody, err := io.ReadAll(resp.Body)
  142. if err != nil {
  143. global.FILE_LOG.Error("http请求失败,读取body失败:" + err.Error())
  144. return
  145. }
  146. if resp.StatusCode != http.StatusOK {
  147. global.FILE_LOG.Error("http请求异常,请求状态为:" + resp.Status)
  148. return
  149. }
  150. global.FILE_LOG.Info("htt请求成功,body:" + string(repBody))
  151. return
  152. }
  153. // RetryStrategy 接口定义
  154. type RetryStrategy interface {
  155. ShouldRetry(attempt int, err error) bool
  156. RetryDelay(attempt int) time.Duration
  157. }
  158. // FixedRetryStrategy 实现 RetryStrategy 接口
  159. type FixedRetryStrategy struct {
  160. maxAttempts int
  161. delay time.Duration
  162. }
  163. // NewFixedRetryStrategy 创建 FixedRetryStrategy 实例
  164. func newFixedRetryStrategy(maxAttempts int, delay time.Duration) *FixedRetryStrategy {
  165. return &FixedRetryStrategy{
  166. maxAttempts: maxAttempts,
  167. delay: delay,
  168. }
  169. }
  170. // ShouldRetry 判断是否应该重试
  171. func (s *FixedRetryStrategy) ShouldRetry(attempt int, err error) bool {
  172. return attempt < s.maxAttempts && retryErr(err)
  173. }
  174. func (s *FixedRetryStrategy) Header() {
  175. }
  176. func retryErr(err error) bool {
  177. return errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled)
  178. }
  179. // RetryDelay 返回重试延迟时间
  180. func (s *FixedRetryStrategy) RetryDelay(attempt int) time.Duration {
  181. return time.Duration(attempt) * s.delay
  182. }
  183. func CreateDefault[T any]() *DefaultHttpClient[T] {
  184. return &DefaultHttpClient[T]{
  185. client: &http.Client{Timeout: 10 * time.Second},
  186. retryStrategy: newFixedRetryStrategy(3, 3*time.Second),
  187. }
  188. }