提交 872999fc authored 作者: mooncake's avatar mooncake

update

上级 c1638cdf
......@@ -3,6 +3,7 @@ module gitlab.wanzhuangkj.com/tush/opkg
go 1.24.3
require (
github.com/alicebob/miniredis/v2 v2.35.0
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
github.com/bwmarrin/snowflake v0.3.0
github.com/felixge/fgprof v0.9.3
......@@ -16,6 +17,7 @@ require (
github.com/mojocn/base64Captcha v1.3.8
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.19.1
github.com/redis/go-redis/extra/redisotel/v9 v9.16.0
github.com/redis/go-redis/v9 v9.16.0
github.com/shirou/gopsutil/v3 v3.24.5
github.com/spf13/cast v1.10.0
......@@ -23,6 +25,7 @@ require (
github.com/uptrace/opentelemetry-go-extra/otelgorm v0.3.2
go.opentelemetry.io/contrib v1.38.0
go.opentelemetry.io/otel v1.37.0
go.opentelemetry.io/otel/sdk v1.37.0
go.opentelemetry.io/otel/trace v1.37.0
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.40.0
......@@ -60,6 +63,7 @@ require (
github.com/goccy/go-yaml v1.18.0 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
......@@ -84,12 +88,14 @@ require (
github.com/prometheus/procfs v0.12.0 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.54.0 // indirect
github.com/redis/go-redis/extra/rediscmd/v9 v9.16.0 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.0 // indirect
github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2 // indirect
github.com/yuin/gopher-lua v1.1.1 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel/metric v1.37.0 // indirect
......
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/alicebob/miniredis/v2 v2.35.0 h1:QwLphYqCEAo1eu1TqPRN2jgVMPBweeQcR21jeqDCONI=
github.com/alicebob/miniredis/v2 v2.35.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM=
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g=
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
......@@ -85,6 +87,8 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
......@@ -150,6 +154,10 @@ github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg=
github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
github.com/redis/go-redis/extra/rediscmd/v9 v9.16.0 h1:zAFQyFxJ3QDwpPUY/CKn22LI5+B8m/lUyffzq2+8ENs=
github.com/redis/go-redis/extra/rediscmd/v9 v9.16.0/go.mod h1:ouOc8ujB2wdUG6o0RrqaPl2tI6cenExC0KkJQ+PHXmw=
github.com/redis/go-redis/extra/redisotel/v9 v9.16.0 h1:+a9h9qxFXdf3gX0FXnDcz7X44ZBFUPq58Gblq7aMU4s=
github.com/redis/go-redis/extra/redisotel/v9 v9.16.0/go.mod h1:EtTTC7vnKWgznfG6kBgl9ySLqd7NckRCFUBzVXdeHeI=
github.com/redis/go-redis/v9 v9.16.0 h1:OotgqgLSRCmzfqChbQyG1PHC3tLNR89DG4jdOERSEP4=
github.com/redis/go-redis/v9 v9.16.0/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=
github.com/redis/rueidis v1.0.64 h1:XqgbueDuNV3qFdVdQwAHJl1uNt90zUuAJuzqjH4cw6Y=
......@@ -191,6 +199,8 @@ github.com/uptrace/opentelemetry-go-extra/otelgorm v0.3.2/go.mod h1:wocb5pNrj/sj
github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2 h1:ZjUj9BLYf9PEqBn8W/OapxhPjVRdC6CsXTdULHsyk5c=
github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2/go.mod h1:O8bHQfyinKwTXKkiKNGmLQS7vRsqRxIQTFZpYpHK3IQ=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
......@@ -201,6 +211,8 @@ go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
......
// Package goredis is a library wrapped on top of github.com/go-redis/redis.
package goredis
import (
"context"
"errors"
"strings"
"time"
"github.com/redis/go-redis/extra/redisotel/v9"
"github.com/redis/go-redis/v9"
)
// Client is a redis client
type Client = redis.Client
const (
// ErrRedisNotFound not exist in redis
ErrRedisNotFound = redis.Nil
// DefaultRedisName default redis name
DefaultRedisName = "default"
)
// Init connecting to redis
// dsn supported formats.
// (1) no password, no db: localhost:6379
// (2) with password and db: <user>:<pass>@localhost:6379/2
// (3) redis://default:123456@localhost:6379/0?max_retries=3
// for more parameters see the redis source code for the setupConnParams function
func Init(dsn string, opts ...Option) (*redis.Client, error) {
o := defaultOptions()
o.apply(opts...)
opt, err := getRedisOpt(dsn, o)
if err != nil {
return nil, err
}
// replace single options if provided
if o.singleOptions != nil {
opt = o.singleOptions
}
rdb := redis.NewClient(opt)
if o.logger != nil {
rdb.AddHook(NewLogHook(o.logger))
}
if o.tracerProvider != nil {
err = redisotel.InstrumentTracing(rdb, redisotel.WithTracerProvider(o.tracerProvider))
if err != nil {
return nil, err
}
}
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) //nolint
defer cancel()
err = rdb.Ping(ctx).Err()
return rdb, err
}
// InitSingle connecting to single redis instance
func InitSingle(addr string, password string, db int, opts ...Option) (*redis.Client, error) {
o := defaultOptions()
o.apply(opts...)
opt := &redis.Options{
Addr: addr,
Password: password,
DB: db,
DialTimeout: o.dialTimeout,
ReadTimeout: o.readTimeout,
WriteTimeout: o.writeTimeout,
TLSConfig: o.tlsConfig,
}
// replace single options if provided
if o.singleOptions != nil {
opt = o.singleOptions
}
rdb := redis.NewClient(opt)
if o.tracerProvider != nil {
err := redisotel.InstrumentTracing(rdb, redisotel.WithTracerProvider(o.tracerProvider))
if err != nil {
return nil, err
}
}
ctx, _ := context.WithTimeout(context.Background(), 15*time.Second) //nolint
err := rdb.Ping(ctx).Err()
return rdb, err
}
// InitSentinel connecting to redis for sentinel, all redis username and password are the same
func InitSentinel(masterName string, addrs []string, username string, password string, opts ...Option) (*redis.Client, error) {
o := defaultOptions()
o.apply(opts...)
opt := &redis.FailoverOptions{
MasterName: masterName,
SentinelAddrs: addrs,
Username: username,
Password: password,
DialTimeout: o.dialTimeout,
ReadTimeout: o.readTimeout,
WriteTimeout: o.writeTimeout,
TLSConfig: o.tlsConfig,
}
// replace sentinel options if provided
if o.sentinelOptions != nil {
opt = o.sentinelOptions
}
rdb := redis.NewFailoverClient(opt)
if o.tracerProvider != nil {
err := redisotel.InstrumentTracing(rdb, redisotel.WithTracerProvider(o.tracerProvider))
if err != nil {
return nil, err
}
}
ctx, _ := context.WithTimeout(context.Background(), 15*time.Second) //nolint
err := rdb.Ping(ctx).Err()
return rdb, err
}
// InitCluster connecting to redis for cluster, all redis username and password are the same
func InitCluster(addrs []string, username string, password string, opts ...Option) (*redis.ClusterClient, error) {
o := defaultOptions()
o.apply(opts...)
opt := &redis.ClusterOptions{
Addrs: addrs,
Username: username,
Password: password,
DialTimeout: o.dialTimeout,
ReadTimeout: o.readTimeout,
WriteTimeout: o.writeTimeout,
TLSConfig: o.tlsConfig,
}
// replace cluster options if provided
if o.clusterOptions != nil {
opt = o.clusterOptions
}
clusterRdb := redis.NewClusterClient(opt)
if o.tracerProvider != nil {
err := redisotel.InstrumentTracing(clusterRdb, redisotel.WithTracerProvider(o.tracerProvider))
if err != nil {
return nil, err
}
}
ctx, _ := context.WithTimeout(context.Background(), 15*time.Second) //nolint
err := clusterRdb.ForEachMaster(ctx, func(ctx context.Context, client *redis.Client) error {
return client.Ping(ctx).Err()
})
return clusterRdb, err
}
func getRedisOpt(dsn string, opts *options) (*redis.Options, error) {
dsn = strings.ReplaceAll(dsn, " ", "")
if len(dsn) > 8 {
if !strings.Contains(dsn[len(dsn)-3:], "/") {
dsn += "/0" // use db 0 by default
}
if dsn[:8] != "redis://" && dsn[:9] != "rediss://" {
dsn = "redis://" + dsn
}
}
redisOpts, err := redis.ParseURL(dsn)
if err != nil {
return nil, err
}
if opts.dialTimeout > 0 {
redisOpts.DialTimeout = opts.dialTimeout
}
if opts.readTimeout > 0 {
redisOpts.ReadTimeout = opts.readTimeout
}
if opts.writeTimeout > 0 {
redisOpts.WriteTimeout = opts.writeTimeout
}
if opts.tlsConfig != nil {
redisOpts.TLSConfig = opts.tlsConfig
}
return redisOpts, nil
}
// Close redis client
func Close(rdb *redis.Client) error {
if rdb == nil {
return nil
}
err := rdb.Close()
if err != nil && errors.Is(err, redis.ErrClosed) {
return err
}
return nil
}
// CloseCluster redis cluster client
func CloseCluster(clusterRdb *redis.ClusterClient) error {
if clusterRdb == nil {
return nil
}
err := clusterRdb.Close()
if err != nil && errors.Is(err, redis.ErrClosed) {
return err
}
return nil
}
package goredis
import (
"fmt"
"testing"
"time"
"github.com/alicebob/miniredis/v2"
"github.com/stretchr/testify/assert"
)
func TestInit(t *testing.T) {
redisServer, _ := miniredis.Run()
defer redisServer.Close()
addr := redisServer.Addr()
type args struct {
redisURL string
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: " no password, no db",
args: args{addr},
wantErr: false,
},
{
name: "has password, no db",
args: args{"root:123456@" + addr},
wantErr: false,
},
{
name: "no password, has db",
args: args{addr + "/5"},
wantErr: false,
},
{
name: "has password, has db",
args: args{fmt.Sprintf("root:123456@%s/5", addr)},
wantErr: false,
},
{
name: "has redis prefix",
args: args{fmt.Sprintf("redis://root:123456@%s/5", addr)},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
rdb, err := Init(tt.args.redisURL,
WithDialTimeout(time.Second),
WithReadTimeout(time.Second),
WithWriteTimeout(time.Second),
WithEnableTrace(),
WithTracing(nil), // nil means no set field
WithTLSConfig(nil), // nil means no set field
)
if (err != nil) != tt.wantErr {
t.Logf("error = %v, wantErr %v", err, tt.wantErr)
return
}
defer Close(rdb)
assert.NotNil(t, rdb)
})
}
}
func TestInitSingle(t *testing.T) {
redisServer, _ := miniredis.Run()
defer redisServer.Close()
addr := redisServer.Addr()
rdb, err := InitSingle(addr, "", 0,
WithDialTimeout(time.Second),
WithReadTimeout(time.Second),
WithWriteTimeout(time.Second),
WithTracing(nil), // nil means no set field
WithTLSConfig(nil), // nil means no set field
WithSingleOptions(nil), // nil means no set field
)
assert.Nil(t, err)
assert.NotNil(t, rdb)
}
func TestInitSentinel(t *testing.T) {
redisServer, _ := miniredis.Run()
defer redisServer.Close()
addr := redisServer.Addr()
rdb, err := InitSentinel("mymaster", []string{addr}, "", "",
WithDialTimeout(time.Second),
WithReadTimeout(time.Second),
WithWriteTimeout(time.Second),
WithTracing(nil), // nil means no set field
WithTLSConfig(nil), // nil means no set field
WithSentinelOptions(nil), // nil means no set field
)
t.Log(err)
assert.NotNil(t, rdb)
}
func TestInitCluster(t *testing.T) {
redisServer, _ := miniredis.Run()
defer redisServer.Close()
addr := redisServer.Addr()
clusterRdb, err := InitCluster([]string{addr}, "", "",
WithDialTimeout(time.Second*15),
WithReadTimeout(time.Second),
WithWriteTimeout(time.Second),
WithTracing(nil), // nil means no set field
WithTLSConfig(nil), // nil means no set field
WithClusterOptions(nil), // nil means no set field
)
defer CloseCluster(clusterRdb)
assert.Nil(t, err)
assert.NotNil(t, clusterRdb)
}
package goredis
import (
"context"
"fmt"
"net"
"runtime"
"time"
"gitlab.wanzhuangkj.com/tush/opkg/pkg/logger"
ctxUtil "gitlab.wanzhuangkj.com/tush/opkg/xutils/ctxutils"
"github.com/redis/go-redis/v9"
"go.uber.org/zap"
)
type LogHook struct {
logger *zap.Logger
}
func NewLogHook(logger *zap.Logger) LogHook {
return LogHook{
logger: logger,
}
}
func (LogHook) DialHook(next redis.DialHook) redis.DialHook {
return func(ctx context.Context, network, addr string) (net.Conn, error) {
return next(ctx, network, addr)
}
}
const max = 64
const logPrefix = "[redis]"
func (x LogHook) ProcessHook(next redis.ProcessHook) redis.ProcessHook {
return func(ctx context.Context, cmd redis.Cmder) error {
st := time.Now()
f, l, fn := caller()
line := fmt.Sprintf("%s %s:%d ", fn, f, l)
err := next(ctx, cmd)
if err != nil {
if x.logger != nil {
x.logger.Info(Pretty(fmt.Sprintf("%s %s", logPrefix, cmd.String()), max),
logger.String("result", "fail"),
logger.Any("err", err),
logger.String("cost", time.Since(st).String()),
logger.String("line", line),
ctxUtil.CtxTraceIDField(ctx))
}
return err
}
if x.logger != nil {
x.logger.Info(Pretty(fmt.Sprintf("%s %s", logPrefix, cmd.String()), max),
logger.String("result", "no"),
logger.String("cost", time.Since(st).String()),
logger.String("line", line),
ctxUtil.CtxTraceIDField(ctx))
}
return nil
}
}
func (LogHook) ProcessPipelineHook(next redis.ProcessPipelineHook) redis.ProcessPipelineHook {
return func(ctx context.Context, cmds []redis.Cmder) error {
return next(ctx, cmds)
}
}
func Pretty(str string, limit int) string {
if len(str) > limit {
return str[:limit] + "......"
}
return str
}
func caller() (string, int, string) {
pc, file, line, ok := runtime.Caller(6)
if ok {
fn := runtime.FuncForPC(pc)
return file, line, fn.Name()
}
return file, line, ""
}
package goredis
import (
"crypto/tls"
"time"
"github.com/redis/go-redis/v9"
"go.opentelemetry.io/otel/sdk/trace"
"go.uber.org/zap"
)
// Option set the redis options.
type Option func(*options)
type options struct {
dialTimeout time.Duration
readTimeout time.Duration
writeTimeout time.Duration
tlsConfig *tls.Config
// Note: this field is only used for Init and InitSingle, and the other parameters will be ignored.
singleOptions *redis.Options
// Note: this field is only used for InitSentinel, and the other parameters will be ignored.
sentinelOptions *redis.FailoverOptions
// Note: this field is only used for InitCluster, and the other parameters will be ignored.
clusterOptions *redis.ClusterOptions
// deprecated: use tp instead
enableTrace bool
tracerProvider *trace.TracerProvider
logger *zap.Logger
}
func (o *options) apply(opts ...Option) {
for _, opt := range opts {
opt(o)
}
}
// default settings
func defaultOptions() *options {
return &options{
enableTrace: false, // whether to enable trace, default off
}
}
// WithEnableTrace use trace, redis v8
// Deprecated: use WithEnableTracer instead
func WithEnableTrace() Option {
return func(o *options) {
o.enableTrace = true
}
}
func WithLogger(l *zap.Logger) Option {
return func(o *options) {
o.logger = l
}
}
// WithTracing set redis tracer provider, redis v9
func WithTracing(tp *trace.TracerProvider) Option {
return func(o *options) {
o.tracerProvider = tp
}
}
// WithDialTimeout set dail timeout
func WithDialTimeout(t time.Duration) Option {
return func(o *options) {
o.dialTimeout = t
}
}
// WithReadTimeout set read timeout
func WithReadTimeout(t time.Duration) Option {
return func(o *options) {
o.readTimeout = t
}
}
// WithWriteTimeout set write timeout
func WithWriteTimeout(t time.Duration) Option {
return func(o *options) {
o.writeTimeout = t
}
}
// WithTLSConfig set TLS config
func WithTLSConfig(c *tls.Config) Option {
return func(o *options) {
o.tlsConfig = c
}
}
// WithSingleOptions set single redis options
func WithSingleOptions(opt *redis.Options) Option {
return func(o *options) {
o.singleOptions = opt
}
}
// WithSentinelOptions set redis sentinel options
func WithSentinelOptions(opt *redis.FailoverOptions) Option {
return func(o *options) {
o.sentinelOptions = opt
}
}
// WithClusterOptions set redis cluster options
func WithClusterOptions(opt *redis.ClusterOptions) Option {
return func(o *options) {
o.clusterOptions = opt
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论