feat: slow threshold customizable in redis (#1185)

* feat: slow threshold customizable in redis

* chore: improve config robustness
This commit is contained in:
Kevin Wan 2021-10-31 22:14:20 +08:00 committed by GitHub
parent b4d1c6da2c
commit 429f85a9de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 83 additions and 33 deletions

16
core/conf/time.go Normal file
View File

@ -0,0 +1,16 @@
package conf
import "time"
const minDuration = 100 * time.Microsecond
// CheckedDuration returns the duration that guaranteed to be greater than 100us.
// Why we need this is because users sometimes intend to use 500 to represent 500ms.
// In config, duration less than 100us should always be missing ms etc.
func CheckedDuration(duration time.Duration) time.Duration {
if duration > minDuration {
return duration
}
return duration * time.Millisecond
}

14
core/conf/time_test.go Normal file
View File

@ -0,0 +1,14 @@
package conf
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestCheckedDuration(t *testing.T) {
assert.Equal(t, time.Second, CheckedDuration(1000))
assert.Equal(t, 2*time.Second, CheckedDuration(2000))
assert.Equal(t, 2*time.Second, CheckedDuration(time.Second*2))
}

View File

@ -1,6 +1,11 @@
package redis package redis
import "errors" import (
"errors"
"time"
"github.com/tal-tech/go-zero/core/conf"
)
var ( var (
// ErrEmptyHost is an error that indicates no redis host is set. // ErrEmptyHost is an error that indicates no redis host is set.
@ -14,10 +19,11 @@ var (
type ( type (
// A RedisConf is a redis config. // A RedisConf is a redis config.
RedisConf struct { RedisConf struct {
Host string Host string
Type string `json:",default=node,options=node|cluster"` Type string `json:",default=node,options=node|cluster"`
Pass string `json:",optional"` Pass string `json:",optional"`
Tls bool `json:",default=false,options=true|false"` Tls bool `json:",default=false,options=true|false"`
SlowThreshold time.Duration `json:",default=100ms"`
} }
// A RedisKeyConf is a redis config with key. // A RedisKeyConf is a redis config with key.
@ -36,6 +42,9 @@ func (rc RedisConf) NewRedis() *Redis {
if len(rc.Pass) > 0 { if len(rc.Pass) > 0 {
opts = append(opts, WithPass(rc.Pass)) opts = append(opts, WithPass(rc.Pass))
} }
if rc.SlowThreshold > 0 {
opts = append(opts, WithSlowThreshold(conf.CheckedDuration(rc.SlowThreshold)))
}
if rc.Tls { if rc.Tls {
opts = append(opts, WithTLS()) opts = append(opts, WithTLS())
} }

View File

@ -2,6 +2,7 @@ package redis
import ( import (
"strings" "strings"
"time"
red "github.com/go-redis/redis" red "github.com/go-redis/redis"
"github.com/tal-tech/go-zero/core/logx" "github.com/tal-tech/go-zero/core/logx"
@ -9,24 +10,26 @@ import (
"github.com/tal-tech/go-zero/core/timex" "github.com/tal-tech/go-zero/core/timex"
) )
func process(proc func(red.Cmder) error) func(red.Cmder) error { func checkDuration(slowThreshold time.Duration) func(proc func(red.Cmder) error) func(red.Cmder) error {
return func(cmd red.Cmder) error { return func(proc func(red.Cmder) error) func(red.Cmder) error {
start := timex.Now() return func(cmd red.Cmder) error {
start := timex.Now()
defer func() { defer func() {
duration := timex.Since(start) duration := timex.Since(start)
if duration > slowThreshold { if duration > slowThreshold {
var buf strings.Builder var buf strings.Builder
for i, arg := range cmd.Args() { for i, arg := range cmd.Args() {
if i > 0 { if i > 0 {
buf.WriteByte(' ') buf.WriteByte(' ')
}
buf.WriteString(mapping.Repr(arg))
} }
buf.WriteString(mapping.Repr(arg)) logx.WithDuration(duration).Slowf("[REDIS] slowcall on executing: %s", buf.String())
} }
logx.WithDuration(duration).Slowf("[REDIS] slowcall on executing: %s", buf.String()) }()
}
}()
return proc(cmd) return proc(cmd)
}
} }
} }

View File

@ -21,8 +21,7 @@ const (
blockingQueryTimeout = 5 * time.Second blockingQueryTimeout = 5 * time.Second
readWriteTimeout = 2 * time.Second readWriteTimeout = 2 * time.Second
defaultSlowThreshold = time.Millisecond * 100
slowThreshold = time.Millisecond * 100
) )
// ErrNilNode is an error that indicates a nil redis node. // ErrNilNode is an error that indicates a nil redis node.
@ -40,11 +39,12 @@ type (
// Redis defines a redis node/cluster. It is thread-safe. // Redis defines a redis node/cluster. It is thread-safe.
Redis struct { Redis struct {
Addr string Addr string
Type string Type string
Pass string Pass string
tls bool tls bool
brk breaker.Breaker brk breaker.Breaker
slowThreshold time.Duration
} }
// RedisNode interface represents a redis node. // RedisNode interface represents a redis node.
@ -78,9 +78,10 @@ type (
// New returns a Redis with given options. // New returns a Redis with given options.
func New(addr string, opts ...Option) *Redis { func New(addr string, opts ...Option) *Redis {
r := &Redis{ r := &Redis{
Addr: addr, Addr: addr,
Type: NodeType, Type: NodeType,
brk: breaker.NewBreaker(), brk: breaker.NewBreaker(),
slowThreshold: defaultSlowThreshold,
} }
for _, opt := range opts { for _, opt := range opts {
@ -1765,6 +1766,13 @@ func WithPass(pass string) Option {
} }
} }
// WithSlowThreshold sets the slow threshold.
func WithSlowThreshold(threshold time.Duration) Option {
return func(r *Redis) {
r.slowThreshold = threshold
}
}
// WithTLS customizes the given Redis with TLS enabled. // WithTLS customizes the given Redis with TLS enabled.
func WithTLS() Option { func WithTLS() Option {
return func(r *Redis) { return func(r *Redis) {

View File

@ -1115,7 +1115,7 @@ func runOnRedisTLS(t *testing.T, fn func(client *Redis)) {
client.Close() client.Close()
} }
}() }()
fn(New(s.Addr(), WithTLS())) fn(New(s.Addr(), WithTLS(), WithSlowThreshold(defaultSlowThreshold/2)))
} }
func badType() Option { func badType() Option {

View File

@ -32,7 +32,7 @@ func getClient(r *Redis) (*red.Client, error) {
MinIdleConns: idleConns, MinIdleConns: idleConns,
TLSConfig: tlsConfig, TLSConfig: tlsConfig,
}) })
store.WrapProcess(process) store.WrapProcess(checkDuration(r.slowThreshold))
return store, nil return store, nil
}) })
if err != nil { if err != nil {

View File

@ -25,7 +25,7 @@ func getCluster(r *Redis) (*red.ClusterClient, error) {
MinIdleConns: idleConns, MinIdleConns: idleConns,
TLSConfig: tlsConfig, TLSConfig: tlsConfig,
}) })
store.WrapProcess(process) store.WrapProcess(checkDuration(r.slowThreshold))
return store, nil return store, nil
}) })