# Redis

# 1 简介

go-redis (opens new window) 进行了轻量封装,并提供了以下功能:

  • 规范了标准配置格式,提供了统一的 Load().Build() 方法。
  • 支持自定义拦截器
  • 提供了默认的 Debug 拦截器,开启 Debug 后可输出 Request、Response 至终端。
  • 提供了默认的 Metric 拦截器,开启后可采集 Prometheus 指标数据
  • 提供了redis的分布式锁
  • 提供了redis的分布式锁的定时任务

# 2 说明

# 3 使用方式

go get github.com/ego-component/eredis
1

# 4 Redis配置

type config struct {
    Addrs                      []string      // Addrs 实例配置地址
    Addr                       string        // Addr stubConfig 实例配置地址
    Mode                       string        // Mode Redis模式 cluster|stub|sentinel
    MasterName                 string        // MasterName 哨兵主节点名称,sentinel模式下需要配置此项
    Password                   string        // Password 密码
    DB                         int           // DB,默认为0, 一般应用不推荐使用DB分片
    PoolSize                   int           // PoolSize 集群内每个节点的最大连接池限制 默认每个CPU10个连接
    MaxRetries                 int           // MaxRetries 网络相关的错误最大重试次数 默认8次
    MinIdleConns               int           // MinIdleConns 最小空闲连接数
    DialTimeout                time.Duration // DialTimeout 拨超时时间
    ReadTimeout                time.Duration // ReadTimeout 读超时 默认3s
    WriteTimeout               time.Duration // WriteTimeout 读超时 默认3s
    IdleTimeout                time.Duration // IdleTimeout 连接最大空闲时间,默认60s, 超过该时间,连接会被主动关闭
    Debug                      bool          // Debug开关, 是否开启调试,默认不开启,开启后并加上export EGO_DEBUG=true,可以看到每次请求,配置名、地址、耗时、请求数据、响应数据
    ReadOnly                   bool          // ReadOnly 集群模式 在从属节点上启用读模式
    SlowLogThreshold           time.Duration // 慢日志门限值,超过该门限值的请求,将被记录到慢日志中
    OnFail                     string        // OnFail panic|error
    EnableMetricInterceptor    bool          // 是否开启监控,默认开启
    EnableTraceInterceptor     bool          // 是否开启链路,默认开启
    EnableAccessInterceptor    bool          // 是否开启,记录请求数据
    EnableAccessInterceptorReq bool          // 是否开启记录请求参数
    EnableAccessInterceptorRes bool          // 是否开启记录响应参数
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 5 普通Redis查询

# 5.1 用户配置

# 常用的stub单实例配置示例
[redis.stub]
   debug = true # ego增加redis debug,打开后可以看到,配置名、地址、耗时、请求数据、响应数据
   mode = "stub" # 默认为stub单实例模式,可选"stub|cluster|sentinel"
   addr = "127.0.0.1:6379"

# cluster集群模式配置示例
[redis.cluster]
   debug = true
   mode = "cluster" # 设置为"cluster"模式,该模式下必须配置"addrs" 
   addrs = ["127.0.0.1:6379", "127.0.0.1:6380", "127.0.0.1:6381"]  # cluster模式下必须配置"addrs"

# sentinel哨兵模式配置示例
[redis.sentinel]
   debug = true
   mode = "sentinel" # 设置为"sentinel"模式,该模式下必须配置"addrs"和"masterName"
   addrs = ["127.0.0.1:26379", "127.0.0.1:26380", "127.0.0.1:26381"] # sentinel模式下必须配置"addrs"
   masterName = "my-sentinel-master-name" # sentinel 模式下必须配置"masterName"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 5.2 优雅的Debug

通过开启 debug 配置和命令行的 export EGO_DEBUG=true,我们就可以在测试环境里看到请求里的配置名、地址、耗时、请求数据、响应数据

image

# 5.3 用户代码

配置创建一个 redis 的配置项,其中内容按照上文配置进行填写。以上这个示例里这个配置key是redis.test

代码中创建一个 redis 实例 eredis.Load("key").Build(),代码中的 key 和配置中的 key 要保持一致。创建完 redis 实例后,就可以直接使用他对 redis 进行 crud

package main
     
import (
    "fmt"

    "github.com/gotomicro/ego"
    "github.com/gotomicro/ego-component/eredis"
    "github.com/gotomicro/ego/core/elog"
)

// export EGO_DEBUG=true && go run main.go --config=config.toml
func main() {
    err := ego.New().Invoker(
        invokerRedis,
        testRedis,
    ).Run()
    if err != nil {
        elog.Panic("startup", elog.FieldErr(err))
    }
}

var eredisStubClient *eredis.Component
var eredisClusterClient *eredis.Component
var eredisSentinelClient *eredis.Component

func invokerRedis() error {
    // 读取"redis.stub"配置,并初始化redis stub component实例
    eredisStubClient = eredis.Load("redis.stub").Build()
    // 读取"redis.cluster"配置,并初始化redis cluster component实例
    eredisClusterClient = eredis.Load("redis.cluster").Build()
    // 读取"redis.sentinel"配置,并初始化redis sentinel component实例
    eredisSentinelClient = eredis.Load("redis.sentinel").Build()
    return nil
}

func testRedis() error {
    // 使用redis stub component进行set、get操作
    err := eredisStubClient.Set(context.Background(), "hello", "world", 0)
    if err != nil {
        log.Println(err)
    }
    str, err := eredisStubClient.Get(context.Background(), "hello")
    if err != nil {
        log.Println(err)
    }
    fmt.Println(str)

    // 使用redis cluster component进行set、get操作
    err := eredisClusterClient.Set(context.Background(), "hello", "world", 0)
    if err != nil {
        log.Println(err)
    }
    str, err := eredisClusterClient.Get(context.Background(), "hello")
    if err != nil {
        log.Println(err)
    }
    fmt.Println(str)
    return nil

    // 使用redis sentinel component进行set、get操作
    err := eredisSentinelClient.Set(context.Background(), "hello", "world", 0)
    if err != nil {
        log.Println(err)
    }
    str, err := eredisSentinelClient.Get(context.Background(), "hello")
    if err != nil {
        log.Println(err)
    }
    fmt.Println(str)
    return nil
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

# 6 Redis的日志

任何redis的请求都会记录redis的错误access日志,如果需要对redis的日志做定制化处理,可参考以下使用方式。

# 6.1 开启redis的access日志

线上在并发量不高,或者核心业务下可以开启全量access日志,这样方便我们排查问题

# 6.2 开启日志方式

在原有的redis配置中,加入以下三行配置,redis的日志里就会记录响应的数据

[redis.test]
enableAccessInterceptor=true       # 是否开启,记录请求数据
enableAccessInterceptorReq=true    # 是否开启记录请求参数
enableAccessInterceptorRes=true    # 是否开启记录响应参数
1
2
3
4

img.png

# 6.3 开启自定义日志字段的数据

在使用了ego的自定义字段功能export EGO_LOG_EXTRA_KEYS=X-Ego-Uid,将对应的数据塞入到context中,那么redis的access日志就可以记录对应字段信息。 参考 详细文档 (opens new window)

func testRedis() error {
    ctx := context.Background()
    ctx = context.WithValue(ctx, "X-Ego-Uid", 9527)
    err := eredisClient.Set(ctx, "hello", "world", 0)
    fmt.Println("set hello", err)
    
    str, err := eredisClient.Get(ctx, "hello")
    fmt.Println("get hello", str, err)
    
    str, err = eredisClient.Get(ctx, "lee")
    fmt.Println("Get lee", errors.Is(err, eredis.Nil), "err="+err.Error())
    
    return nil
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 7 Redis的定时任务锁

// export EGO_DEBUG=true && go run main.go --config=config.toml
func main() {
	err := ego.New().Invoker(initRedis).Cron(cronJob()).Run()
	if err != nil {
		elog.Panic("startup", elog.FieldErr(err))
	}
}

func initRedis() error {
	redis = eredis.Load("redis.test").Build()
	return nil
}

func cronJob() ecron.Ecron {
	locker := ecronlock.DefaultContainer().Build(ecronlock.WithClient(redis))
	cron := ecron.Load("cron.default").Build(
		ecron.WithLock(locker.NewLock("ego-component:cronjob:syncXxx")),
		ecron.WithJob(helloWorld),
	)
	return cron
}

func helloWorld(ctx context.Context) error {
	log.Println("cron job running")
	return nil
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 8 Redis监控

img.png img.png img.png

上次更新: 2024-01-25 09:16:20