数据打点、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/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/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
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