数据打点、Http优化
parent
47a82f1878
commit
a37ea90246
@ -1,5 +1,14 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/go-resty/resty/v2 v2.16.3 h1:zacNT7lt4b8M/io2Ahj6yPypL7bqx9n1iprfQuodV+E=
|
github.com/go-resty/resty/v2 v2.16.3 h1:zacNT7lt4b8M/io2Ahj6yPypL7bqx9n1iprfQuodV+E=
|
||||||
github.com/go-resty/resty/v2 v2.16.3/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
|
github.com/go-resty/resty/v2 v2.16.3/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
|
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
||||||
|
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
|
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||||
|
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||||
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
package point
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gitea.weitiangame.com/sdk/wt-game/utils/ahttp"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
instance *Point
|
||||||
|
once sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
type Point struct {
|
||||||
|
clientID string
|
||||||
|
logger *zap.Logger
|
||||||
|
ahttp *resty.Request
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns the singleton instance of Point.
|
||||||
|
func New(clientID string, logger *zap.Logger) *Point {
|
||||||
|
once.Do(func() {
|
||||||
|
instance = &Point{
|
||||||
|
clientID: clientID,
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
if instance.logger == nil {
|
||||||
|
var err error
|
||||||
|
instance.logger, err = zap.NewProduction()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.ahttp = ahttp.New(nil).SetLog(instance.logger).Client()
|
||||||
|
})
|
||||||
|
|
||||||
|
return instance
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event sends an event to the specified URL.
|
||||||
|
func (p *Point) Event(data map[string]interface{}) (*resty.Response, error) {
|
||||||
|
return p.ahttp.SetBody(data).Post("https://e.tapdb.net/v2/event")
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"gitea.weitiangame.com/sdk/wt-game/point"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var pointSdk = point.New("your-client-id", nil)
|
||||||
|
|
||||||
|
// TestOrderSign 测试订单签名
|
||||||
|
func TestPoint(t *testing.T) {
|
||||||
|
event, err := pointSdk.Event(map[string]interface{}{
|
||||||
|
"test": "test",
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println(event)
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
@ -0,0 +1,167 @@
|
|||||||
|
package ahttp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config 包含 HTTP 客户端的配置选项
|
||||||
|
// Config contains the configuration options for the HTTP client
|
||||||
|
type Config struct {
|
||||||
|
MaxIdleConnections int // 连接池的最大空闲连接数 / Maximum idle connections in the connection pool
|
||||||
|
IdleConnectionTimeout time.Duration // 空闲连接超时时间 / Timeout for idle connections
|
||||||
|
DisableCompression bool // 禁用压缩 / Disable compression
|
||||||
|
DisableKeepAlives bool // 禁用 keep-alive / Disable keep-alive
|
||||||
|
InsecureSkipVerify bool // 跳过 TLS 证书验证 / Skip TLS certificate verification
|
||||||
|
Timeout time.Duration // 总超时时间 / Total timeout duration
|
||||||
|
TLSHandshakeTimeout time.Duration // TLS 握手超时时间 / Timeout for TLS handshake
|
||||||
|
ExpectContinueTimeout time.Duration // 100-continue 超时时间 / Timeout for 100-continue
|
||||||
|
MaxConnectionsPerHost int // 每主机的最大连接数 / Maximum connections per host
|
||||||
|
RetryAttempts int // 请求重试次数 / Number of retry attempts for failed requests
|
||||||
|
DialerTimeout time.Duration // Dialer 的连接超时时间 / Dialer connection timeout
|
||||||
|
DialerKeepAlive time.Duration // Dialer 的 Keep-Alive 时间 / Dialer keep-alive time
|
||||||
|
}
|
||||||
|
|
||||||
|
// HttpClient 是对 Resty 客户端的封装,支持自定义配置和日志 / HttpClient is a wrapper for the Resty client with custom configuration and logging support
|
||||||
|
type HttpClient struct {
|
||||||
|
httpClient *resty.Client // Resty 客户端实例 / Resty client instance
|
||||||
|
httpTransport *http.Transport // HTTP 传输层配置 / HTTP transport layer configuration
|
||||||
|
logger *zap.Logger // 日志记录器 / Logger instance
|
||||||
|
config *Config
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
singletonClient *HttpClient
|
||||||
|
once sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
// New 创建一个新的 HTTP 客户端实例 / NewHttpClient creates a new HTTP client instance
|
||||||
|
func New(config *Config) *HttpClient {
|
||||||
|
once.Do(func() {
|
||||||
|
if config == nil {
|
||||||
|
config = defaultConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
singletonClient = &HttpClient{
|
||||||
|
httpClient: newRestyClient(config),
|
||||||
|
httpTransport: newTransport(config),
|
||||||
|
config: config,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return singletonClient
|
||||||
|
}
|
||||||
|
|
||||||
|
// newRestyClient 配置 Resty 客户端 / Configures the Resty client
|
||||||
|
func newRestyClient(config *Config) *resty.Client {
|
||||||
|
client := resty.NewWithClient(&http.Client{
|
||||||
|
Transport: newTransport(config),
|
||||||
|
Timeout: config.Timeout,
|
||||||
|
})
|
||||||
|
|
||||||
|
client.SetRetryCount(config.RetryAttempts)
|
||||||
|
client.SetRetryWaitTime(1 * time.Second)
|
||||||
|
client.SetRetryMaxWaitTime(10 * time.Second)
|
||||||
|
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultConfig 返回默认的配置值 / Returns the default configuration values
|
||||||
|
func defaultConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
MaxIdleConnections: runtime.GOMAXPROCS(0) * 200,
|
||||||
|
IdleConnectionTimeout: 120 * time.Second,
|
||||||
|
DisableCompression: false,
|
||||||
|
Timeout: 90 * time.Second,
|
||||||
|
TLSHandshakeTimeout: 30 * time.Second,
|
||||||
|
ExpectContinueTimeout: 2 * time.Second,
|
||||||
|
MaxConnectionsPerHost: runtime.GOMAXPROCS(0) * 100,
|
||||||
|
RetryAttempts: 3,
|
||||||
|
DialerTimeout: 15 * time.Second, // 默认 Dialer 超时时间
|
||||||
|
DialerKeepAlive: 60 * time.Second, // 默认 Dialer Keep-Alive 时间
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// newTransport 创建并配置 HTTP 传输层 / Creates and configures the HTTP transport
|
||||||
|
func newTransport(config *Config) *http.Transport {
|
||||||
|
tcpDialer := &net.Dialer{
|
||||||
|
Timeout: config.DialerTimeout,
|
||||||
|
KeepAlive: config.DialerKeepAlive,
|
||||||
|
DualStack: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &http.Transport{
|
||||||
|
Proxy: http.ProxyFromEnvironment,
|
||||||
|
DialContext: tcpDialer.DialContext,
|
||||||
|
ForceAttemptHTTP2: true,
|
||||||
|
MaxIdleConns: config.MaxIdleConnections,
|
||||||
|
MaxIdleConnsPerHost: config.MaxConnectionsPerHost,
|
||||||
|
IdleConnTimeout: config.IdleConnectionTimeout,
|
||||||
|
TLSClientConfig: &tls.Config{
|
||||||
|
InsecureSkipVerify: config.InsecureSkipVerify,
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
|
CurvePreferences: []tls.CurveID{tls.X25519, tls.CurveP256},
|
||||||
|
},
|
||||||
|
TLSHandshakeTimeout: config.TLSHandshakeTimeout,
|
||||||
|
ExpectContinueTimeout: config.ExpectContinueTimeout,
|
||||||
|
DisableCompression: config.DisableCompression,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLog 设置日志记录器 / SetLog sets the logger
|
||||||
|
func (h *HttpClient) SetLog(logger *zap.Logger) *HttpClient {
|
||||||
|
h.logger = logger
|
||||||
|
h.httpClient.OnBeforeRequest(func(c *resty.Client, r *resty.Request) error {
|
||||||
|
logger.Info("Request",
|
||||||
|
zap.String("URL", r.URL),
|
||||||
|
zap.String("Method", r.Method),
|
||||||
|
zap.Any("Headers", r.Header),
|
||||||
|
zap.Any("Cookies", r.Cookies),
|
||||||
|
zap.Any("FormData", r.FormData),
|
||||||
|
zap.Any("QueryParam", r.QueryParam),
|
||||||
|
zap.Any("Body", r.Body),
|
||||||
|
zap.Duration("Timeout", h.config.Timeout))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
h.httpClient.OnAfterResponse(func(c *resty.Client, r *resty.Response) error {
|
||||||
|
if r.StatusCode() >= 400 {
|
||||||
|
logger.Error("Request failed",
|
||||||
|
zap.Int("StatusCode", r.StatusCode()),
|
||||||
|
zap.String("URL", r.Request.URL),
|
||||||
|
zap.String("Method", r.Request.Method),
|
||||||
|
zap.Any("Headers", r.Header),
|
||||||
|
zap.Any("Cookies", r.Cookies),
|
||||||
|
zap.Any("FormData", r.Request.FormData),
|
||||||
|
zap.Any("QueryParam", r.Request.QueryParam),
|
||||||
|
zap.ByteString("Body", r.Body()),
|
||||||
|
zap.Duration("Duration", time.Since(r.Request.Time)),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
logger.Info("Response",
|
||||||
|
zap.Int("StatusCode", r.StatusCode()),
|
||||||
|
zap.String("URL", r.Request.URL),
|
||||||
|
zap.String("Method", r.Request.Method),
|
||||||
|
zap.Any("Headers", r.Header),
|
||||||
|
zap.Any("Cookies", r.Cookies),
|
||||||
|
zap.Any("FormData", r.Request.FormData),
|
||||||
|
zap.Any("QueryParam", r.Request.QueryParam),
|
||||||
|
zap.ByteString("Body", r.Body()),
|
||||||
|
zap.Duration("Duration", time.Since(r.Request.Time)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client 返回 Resty 客户端的请求实例 / Returns a request instance of the Resty client
|
||||||
|
func (h *HttpClient) Client() *resty.Request {
|
||||||
|
h.httpClient.SetHeader("User-Agent", "ahttp")
|
||||||
|
return h.httpClient.R()
|
||||||
|
}
|
Loading…
Reference in New Issue