http_client.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. package httpclent
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "io/ioutil"
  8. "net/http"
  9. "net/url"
  10. "reflect"
  11. "strings"
  12. "time"
  13. "git.shuncheng.lu/bigthing/gocommon/pkg/conf"
  14. "git.shuncheng.lu/bigthing/gocommon/pkg/internal/json"
  15. "git.shuncheng.lu/bigthing/gocommon/pkg/logger"
  16. "git.shuncheng.lu/bigthing/gocommon/pkg/net/qconf"
  17. "git.shuncheng.lu/bigthing/gocommon/pkg/trace"
  18. )
  19. // 请求+抓取异常+解析响应体(比较适合自动解析响应体的,适用于POST/GET/.. 获取响应体为Json的)
  20. func HttpRequestAndDecode(ctx context.Context, serverName string, path string, params interface{}, result interface{}, options ...Option) error {
  21. // 校验绑定的响应类型
  22. if err := validateReceiver(result); err != nil { // result 只支持ptr
  23. return err
  24. }
  25. // 发送请求
  26. response, err := DefaultHttpRequest(ctx, serverName, path, params, options...)
  27. if err != nil {
  28. logger.Errorc(ctx, "[HttpRequest] find err,err=%v", err)
  29. return err
  30. }
  31. // 检测响应体
  32. if err := CheckResponseBodyHasError(response); err != nil {
  33. // 这里不记录 error 日志,根据业务需求记录
  34. return err
  35. }
  36. // 绑定响应数据
  37. err = json.Unmarshal(response, result)
  38. if err != nil {
  39. logger.Errorc(ctx, "[HttpRequest] json decode http response err: %s", err)
  40. return fmtError("[HttpRequest] json decode http response err: %s", err)
  41. }
  42. return nil
  43. }
  44. // 主要是适用于 对于请求参数无需自己绑定/默认校验不符合标准规则/请求参数可能不是json可能是文件的请求
  45. func DefaultHttpRequest(ctx context.Context, serverName string, path string, params interface{}, options ...Option) ([]byte, error) {
  46. beginTime := time.Now()
  47. ctx = context.WithValue(ctx, _ServerName, serverName) // clone,不影响原来的ctx,并发安全
  48. ops := LoadDefaultOp(options...)
  49. initDefaultHttpRequestOp(ops)
  50. defer func() {
  51. FreeOp(ops)
  52. }()
  53. result, err := HttpRequest(ctx, func() (*http.Request, error) {
  54. request, err := newProxyRequest(ctx, serverName, path, params, ops)
  55. if err != nil {
  56. return nil, err
  57. }
  58. logger.Infoc(ctx, "[HttpRequest] start,server_name=%s,url=%s,params=%+v", serverName, request.URL, params)
  59. return request, nil
  60. }, ops)
  61. if err != nil {
  62. return nil, err
  63. }
  64. logger.Infoc(ctx, "[HttpRequest] end,server_name=%s,path=%s,spend=%fs,result=%s", serverName, path, time.Now().Sub(beginTime).Seconds(), result)
  65. return result, err
  66. }
  67. // 内部代理的http的request
  68. func newProxyRequest(ctx context.Context, serverName string, requestPath string, requestParam interface{}, op *Options) (*http.Request, error) {
  69. if op == nil {
  70. return nil, errors.New("[newProxyRequestV2] find error")
  71. }
  72. // 处理请求URL
  73. _url, err := handlerRequestUrl(ctx, serverName, requestPath, op)
  74. if err != nil {
  75. return nil, err
  76. }
  77. // json请求参数
  78. reader, err := handlerRequestParams(ctx, requestParam, op)
  79. if err != nil {
  80. return nil, err
  81. }
  82. // 创建request, 升级api,目前线上编译版本高于1.13,支持传递ctx
  83. request, err := http.NewRequestWithContext(ctx, string(op.HttpMethod), _url.String(), reader)
  84. if err != nil {
  85. return nil, fmtError("[NewRequest] err,err=%+v,url=%s,params=%+v", err, _url, requestParam)
  86. }
  87. // 处理请求
  88. postHandlerRequest(ctx, request, op)
  89. return request, nil
  90. }
  91. // new一个普通的request
  92. func NewHttpRequest(method, url string) (*http.Request, error) {
  93. return http.NewRequest(method, url, nil)
  94. }
  95. // 主要是适用于自定义的更强的
  96. // no cached cookie
  97. func HttpRequest(ctx context.Context, request NewHttpRequestFunc, option *Options) ([]byte, error) {
  98. if option.RetryMaxNum == 0 {
  99. option.RetryMaxNum = defaultRetryNum
  100. }
  101. if option.TimeOut == 0 {
  102. option.TimeOut = defaultTimeOut1s
  103. }
  104. if option.RetryIdleTime == 0 {
  105. option.RetryIdleTime = defaultRetryIdleTime
  106. }
  107. client := http.Client{
  108. Transport: http.DefaultTransport,
  109. Timeout: option.TimeOut,
  110. }
  111. var retryNum = 0
  112. Retry:
  113. response, err := httpDo(ctx, &client, request)
  114. if err != nil {
  115. if retryNum < option.RetryMaxNum {
  116. retryNum++
  117. logger.Warnc(ctx, "[HttpRequest] request %s err,cur_retry=%d,max_retry=%d,err=%v", getServerName(ctx), retryNum, option.RetryMaxNum, err)
  118. time.Sleep(option.RetryIdleTime)
  119. goto Retry
  120. } else {
  121. if response != nil && response.Body != nil {
  122. response.Body.Close()
  123. }
  124. return nil, err
  125. }
  126. }
  127. defer response.Body.Close()
  128. if option.HandlerResponse != nil {
  129. if err := option.HandlerResponse(ctx, response); err != nil {
  130. return nil, err
  131. }
  132. }
  133. body, err := ioutil.ReadAll(response.Body)
  134. if err != nil {
  135. return nil, err
  136. }
  137. return body, nil
  138. }
  139. func httpDo(ctx context.Context, client *http.Client, requestFunc NewHttpRequestFunc) (*http.Response, error) {
  140. request, err := requestFunc()
  141. if err != nil {
  142. return nil, err
  143. }
  144. beginTime := time.Now()
  145. response, err := trace.HttpClientTrace(ctx, client, request, getServerName(ctx))
  146. // 打印第三方调用日志
  147. addThirdLog(ctx, request, response, beginTime)
  148. return response, err
  149. }
  150. func addThirdLog(ctx context.Context, request *http.Request, response *http.Response, beginTime time.Time) {
  151. // 检查 第三方日志开关 是否关闭
  152. if !conf.GetThirdLogWitch() {
  153. return
  154. }
  155. // 打印第三方调用日志
  156. if response != nil {
  157. logger.ThirdLog(request.URL.String(), response.StatusCode, time.Now().Sub(beginTime).Milliseconds(), response.ContentLength, logger.GetTraceId(ctx))
  158. } else {
  159. //response 为 nil, err 一定有值
  160. // 互联网信息服务扩展状态码, 449 Retry With 重新发送
  161. logger.ThirdLog(request.URL.String(), 449, time.Now().Sub(beginTime).Milliseconds(), 0, logger.GetTraceId(ctx))
  162. }
  163. }
  164. func initDefaultHttpRequestOp(op *Options) {
  165. if op.HandlerRequestHeader == nil {
  166. op.HandlerRequestHeader = DefaultHandlerRequestHandler
  167. }
  168. if op.HandlerRequestParams == nil {
  169. op.HandlerRequestParams = DefaultHandlerRequestParams
  170. }
  171. if op.EncodeRequestBody == nil {
  172. op.EncodeRequestBody = DefaultEncodeRequestBody
  173. }
  174. if op.ServerHostUrl == nil {
  175. op.ServerHostUrl = DefaultServerHost
  176. }
  177. if op.Scheme == "" {
  178. op.Scheme = defaultScheme
  179. }
  180. if op.HttpMethod == "" {
  181. op.HttpMethod = defaultMethod
  182. }
  183. }
  184. func handlerRequestUrl(ctx context.Context, host string, requestPath string, op *Options) (*url.URL, error) {
  185. if op.ServerHostUrl != nil {
  186. var err error
  187. if host, err = op.ServerHostUrl(ctx, host); err != nil {
  188. return nil, fmtError("[handlerRequestUrl] err,err=%+v,server_name=%s", err, host)
  189. }
  190. }
  191. _url := url.URL{
  192. Scheme: string(op.Scheme),
  193. Host: host,
  194. Path: requestPath,
  195. }
  196. // 处理请求url
  197. if op.HandlerRequestUrl != nil {
  198. if err := op.HandlerRequestUrl(ctx, &_url); err != nil {
  199. return nil, err
  200. }
  201. }
  202. return &_url, nil
  203. }
  204. func postHandlerRequest(ctx context.Context, req *http.Request, op *Options) {
  205. // 请求头
  206. if op.HandlerRequestHeader != nil {
  207. op.HandlerRequestHeader(ctx, req.Header)
  208. }
  209. // 请求cookie
  210. if op.AddRequestCookies != nil {
  211. cookies := op.AddRequestCookies(ctx, req.Cookies())
  212. for index, _ := range cookies {
  213. req.AddCookie(cookies[index])
  214. }
  215. }
  216. }
  217. // 处理请求参数
  218. func handlerRequestParams(ctx context.Context, req interface{}, op *Options) (io.Reader, error) {
  219. if op.HandlerRequestParams != nil {
  220. req = op.HandlerRequestParams(ctx, req)
  221. }
  222. if op.EncodeRequestBody != nil {
  223. return op.EncodeRequestBody(ctx, req)
  224. }
  225. return nil, nil
  226. }
  227. func GetServerUrl(serverName string) (string, error) {
  228. host := conf.GetString(serverName, "host", "")
  229. //开发环境使用的是ip地址,无法使用qconf
  230. if strings.Compare(conf.GetEnv(), conf.EnvDebug) == 0 {
  231. return host, nil
  232. }
  233. host, err := qconf.GetHost(host, qconf.GetQconfIdc())
  234. if err != nil {
  235. return "", err
  236. }
  237. return host, nil
  238. }
  239. func fmtError(format string, v ...interface{}) error {
  240. return errors.New(fmt.Sprintf(format, v...))
  241. }
  242. // 校验需要绑定的参数,只能指针类型!
  243. func validateReceiver(result interface{}) error {
  244. if result == nil {
  245. return validateNilError
  246. } else {
  247. kind := reflect.TypeOf(result).Kind()
  248. if kind != reflect.Ptr {
  249. return validateTypeError
  250. }
  251. return nil
  252. }
  253. }
  254. func getServerName(ctx context.Context) string {
  255. s, _ := ctx.Value(_ServerName).(string)
  256. return s
  257. }
  258. type HttpRequestInfo struct {
  259. ServiceName string
  260. Path string
  261. Method string
  262. Params interface{}
  263. }
  264. // 兼容老版本!
  265. func NewProxyRequest(requestInfo *HttpRequestInfo, ctx context.Context, op *Options) (*http.Request, error) {
  266. if requestInfo.Method != "" {
  267. op.HttpMethod = HttpMethod(requestInfo.Method)
  268. }
  269. return newProxyRequest(ctx, requestInfo.ServiceName, requestInfo.Path, requestInfo.Params, op)
  270. }