191 lines
5.1 KiB
Go
191 lines
5.1 KiB
Go
|
package httpclient
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"fmt"
|
||
|
"net/http"
|
||
|
"strconv"
|
||
|
"time"
|
||
|
|
||
|
"com.snow.auto_monitor/app/http/trace"
|
||
|
"com.snow.auto_monitor/config"
|
||
|
|
||
|
"github.com/SkyAPM/go2sky"
|
||
|
"github.com/SkyAPM/go2sky/propagation"
|
||
|
v3 "github.com/SkyAPM/go2sky/reporter/grpc/language-agent"
|
||
|
"github.com/go-resty/resty/v2"
|
||
|
"github.com/qit-team/snow-core/log/logger"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
RetryCounts = 2
|
||
|
RetryInterval = 3 * time.Second
|
||
|
)
|
||
|
|
||
|
const componentIDGOHttpClient = 5005
|
||
|
|
||
|
type ClientConfig struct {
|
||
|
ctx context.Context
|
||
|
client *resty.Client
|
||
|
tracer *go2sky.Tracer
|
||
|
extraTags map[string]string
|
||
|
}
|
||
|
|
||
|
type ClientOption func(*ClientConfig)
|
||
|
|
||
|
func WithClientTag(key string, value string) ClientOption {
|
||
|
return func(c *ClientConfig) {
|
||
|
if c.extraTags == nil {
|
||
|
c.extraTags = make(map[string]string)
|
||
|
}
|
||
|
c.extraTags[key] = value
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func WithClient(client *resty.Client) ClientOption {
|
||
|
return func(c *ClientConfig) {
|
||
|
c.client = client
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func WithContext(ctx context.Context) ClientOption {
|
||
|
return func(c *ClientConfig) {
|
||
|
c.ctx = ctx
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type transport struct {
|
||
|
*ClientConfig
|
||
|
delegated http.RoundTripper
|
||
|
}
|
||
|
|
||
|
func (t *transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
|
||
|
span, err := t.tracer.CreateExitSpan(t.ctx, fmt.Sprintf("/%s%s", req.Method, req.URL.Path), req.Host, func(header string) error {
|
||
|
// 将本层的调用链信息写入http头部, 传入到下一层调用, 当前使用v3版本的协议
|
||
|
// https://github.com/apache/skywalking/blob/master/docs/en/protocols/Skywalking-Cross-Process-Propagation-Headers-Protocol-v3.md
|
||
|
req.Header.Set(propagation.Header, header)
|
||
|
return nil
|
||
|
})
|
||
|
if err != nil {
|
||
|
return t.delegated.RoundTrip(req)
|
||
|
}
|
||
|
defer span.End()
|
||
|
span.SetComponent(componentIDGOHttpClient)
|
||
|
for k, v := range t.extraTags {
|
||
|
span.Tag(go2sky.Tag(k), v)
|
||
|
}
|
||
|
span.Tag(go2sky.TagHTTPMethod, req.Method)
|
||
|
span.Tag(go2sky.TagURL, req.URL.String())
|
||
|
span.SetSpanLayer(v3.SpanLayer_Http)
|
||
|
resp, err = t.delegated.RoundTrip(req)
|
||
|
if err != nil {
|
||
|
span.Error(time.Now(), err.Error())
|
||
|
return
|
||
|
}
|
||
|
span.Tag(go2sky.TagStatusCode, strconv.Itoa(resp.StatusCode))
|
||
|
if resp.StatusCode >= http.StatusBadRequest {
|
||
|
span.Error(time.Now(), "Errors on handling client")
|
||
|
}
|
||
|
return resp, nil
|
||
|
}
|
||
|
|
||
|
func NewClient(ctx context.Context, options ...ClientOption) (client *resty.Client) {
|
||
|
client = resty.New()
|
||
|
if config.IsDebug() {
|
||
|
client.SetDebug(true).EnableTrace()
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
tracer *go2sky.Tracer
|
||
|
err error
|
||
|
)
|
||
|
if len(config.GetConf().SkyWalkingOapServer) > 0 && config.IsEnvEqual(config.ProdEnv) {
|
||
|
tracer, err = trace.Tracer()
|
||
|
if err != nil {
|
||
|
logger.Error(ctx, "NewClient:Tracer", err.Error())
|
||
|
}
|
||
|
}
|
||
|
if tracer != nil {
|
||
|
co := &ClientConfig{ctx: ctx, tracer: tracer}
|
||
|
for _, option := range options {
|
||
|
option(co)
|
||
|
}
|
||
|
if co.client == nil {
|
||
|
co.client = client
|
||
|
}
|
||
|
tp := &transport{
|
||
|
ClientConfig: co,
|
||
|
delegated: http.DefaultTransport,
|
||
|
}
|
||
|
if co.client.GetClient().Transport != nil {
|
||
|
tp.delegated = co.client.GetClient().Transport
|
||
|
}
|
||
|
co.client.SetTransport(tp)
|
||
|
}
|
||
|
|
||
|
client.OnBeforeRequest(func(ct *resty.Client, req *resty.Request) error {
|
||
|
//req.SetContext(c)
|
||
|
logger.Info(ctx, "OnBeforeRequest", logger.NewWithField("url", req.URL))
|
||
|
return nil // if its success otherwise return error
|
||
|
})
|
||
|
// Registering Response Middleware
|
||
|
client.OnAfterResponse(func(ct *resty.Client, resp *resty.Response) error {
|
||
|
logger.Info(ctx, "OnAfterResponse", logger.NewWithField("url", resp.Request.URL), logger.NewWithField("request", resp.Request.RawRequest), logger.NewWithField("response", resp.String()))
|
||
|
return nil
|
||
|
})
|
||
|
return client
|
||
|
}
|
||
|
|
||
|
func NewClientWithRetry(ctx context.Context, retryCounts int, retryInterval time.Duration, options ...ClientOption) (client *resty.Client) {
|
||
|
client = resty.New()
|
||
|
if config.IsDebug() {
|
||
|
client.SetDebug(true).EnableTrace()
|
||
|
}
|
||
|
if retryCounts == 0 {
|
||
|
retryCounts = RetryCounts
|
||
|
}
|
||
|
if retryInterval.Seconds() == 0.0 {
|
||
|
retryInterval = RetryInterval
|
||
|
}
|
||
|
client.SetRetryCount(retryCounts).SetRetryMaxWaitTime(retryInterval)
|
||
|
|
||
|
var (
|
||
|
tracer *go2sky.Tracer
|
||
|
err error
|
||
|
)
|
||
|
if len(config.GetConf().SkyWalkingOapServer) > 0 && config.IsEnvEqual(config.ProdEnv) {
|
||
|
tracer, err = trace.Tracer()
|
||
|
if err != nil {
|
||
|
logger.Error(ctx, "NewClient:Tracer", err.Error())
|
||
|
}
|
||
|
}
|
||
|
if tracer != nil {
|
||
|
co := &ClientConfig{ctx: ctx, tracer: tracer}
|
||
|
for _, option := range options {
|
||
|
option(co)
|
||
|
}
|
||
|
if co.client == nil {
|
||
|
co.client = client
|
||
|
}
|
||
|
tp := &transport{
|
||
|
ClientConfig: co,
|
||
|
delegated: http.DefaultTransport,
|
||
|
}
|
||
|
if co.client.GetClient().Transport != nil {
|
||
|
tp.delegated = co.client.GetClient().Transport
|
||
|
}
|
||
|
co.client.SetTransport(tp)
|
||
|
}
|
||
|
|
||
|
client.OnBeforeRequest(func(ct *resty.Client, req *resty.Request) error {
|
||
|
logger.Info(ctx, "OnBeforeRequest", logger.NewWithField("url", req.URL))
|
||
|
return nil // if its success otherwise return error
|
||
|
})
|
||
|
// Registering Response Middleware
|
||
|
client.OnAfterResponse(func(ct *resty.Client, resp *resty.Response) error {
|
||
|
logger.Info(ctx, "OnAfterResponse", logger.NewWithField("url", resp.Request.URL), logger.NewWithField("request", resp.Request.RawRequest), logger.NewWithField("response", resp.String()))
|
||
|
return nil
|
||
|
})
|
||
|
return client
|
||
|
}
|