From cb7dd59b732eb7de220dbf62c02a9ab89f3dc34d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Sat, 27 Nov 2021 16:27:48 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0redis=E7=9B=91=E6=8E=A7?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E7=9A=84=E8=AF=BB=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- middleware/redis/monitor.go | 326 +++++++++++++++++++++++++++++++++ middleware/redis/redis_test.go | 6 + 2 files changed, 332 insertions(+) create mode 100644 middleware/redis/monitor.go diff --git a/middleware/redis/monitor.go b/middleware/redis/monitor.go new file mode 100644 index 0000000..8f4a3aa --- /dev/null +++ b/middleware/redis/monitor.go @@ -0,0 +1,326 @@ +// Package redis ... +// +// Description : redis 系统信息监控 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2021-11-27 12:22 下午 +package redis + +import ( + "context" + "strings" + + "git.zhangdeman.cn/zhangdeman/gopkg/convert" + + yml "gopkg.in/yaml.v2" + + "github.com/go-redis/redis/v8" +) + +// GetRedisServerInfo 获取 redis server info +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 12:24 下午 2021/11/27 +func GetRedisServerInfo(client *redis.Client) (*ServerInfo, error) { + var result ServerInfo + if err := infoToStruct(client, "server", &result); nil != err { + return nil, err + } + return &result, nil +} + +// GetMemoryInfo 获取内存信息 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 12:59 下午 2021/11/27 +func GetMemoryInfo(client *redis.Client) (*MemoryInfo, error) { + var result MemoryInfo + if err := infoToStruct(client, "memory", &result); nil != err { + return nil, err + } + return &result, nil +} + +// GetClientInfo 获取客户端信息 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 1:10 下午 2021/11/27 +func GetClientInfo(client *redis.Client) (*ClientInfo, error) { + var result ClientInfo + if err := infoToStruct(client, "clients", &result); nil != err { + return nil, err + } + return &result, nil +} + +// GetPersistence 获取持久化相关信息 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 1:11 下午 2021/11/27 +func GetPersistence(client *redis.Client) (*Persistence, error) { + var result Persistence + if err := infoToStruct(client, "persistence", &result); nil != err { + return nil, err + } + return &result, nil +} + +// GetStats 获取状态 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 1:19 下午 2021/11/27 +func GetStats(client *redis.Client) (*Stats, error) { + var result Stats + if err := infoToStruct(client, "stats", &result); nil != err { + return nil, err + } + return &result, nil +} + +// GetReplication 复制相关信息 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 1:23 下午 2021/11/27 +func GetReplication(client *redis.Client) (*Replication, error) { + var result Replication + if err := infoToStruct(client, "replication", &result); nil != err { + return nil, err + } + result.SlaveList = GetSlaveList(client) + return &result, nil +} + +// GetSlaveList 获取从库信息 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 4:26 下午 2021/11/27 +func GetSlaveList(client *redis.Client) []SlaveNode { + // 解析从库信息 + slaveList := make([]SlaveNode, 0) + var data map[string]string + _ = infoToStruct(client, "replication", &data) + slaveKey := []string{"slave0", "slave1", "slave2", "slave3", "slave4", "slave5", "slave6", "slave7", "slave8", "slave9"} + for k, v := range data { + isSlave := false + for _, item := range slaveKey { + if strings.Contains(k, item) { + isSlave = true + break + } + } + if !isSlave { + continue + } + + vArr := strings.Split(v, ",") + if len(vArr) < 3 { + continue + } + slave := SlaveNode{ + ID: k, + IP: "", + Port: 0, + State: "", + Offset: 0, + Lag: 0, + } + for _, prop := range vArr { + propArr := strings.Split(prop, "=") + if len(propArr) != 2 { + continue + } + switch strings.ToLower(propArr[0]) { + case "ip": + slave.IP = propArr[1] + case "port": + _ = convert.ConvertAssign(&slave.Port, propArr[1]) + case "state": + slave.State = propArr[1] + case "offset": + _ = convert.ConvertAssign(&slave.Offset, propArr[1]) + case "lag": + _ = convert.ConvertAssign(&slave.Lag, propArr[1]) + } + } + slaveList = append(slaveList, slave) + } + return slaveList +} + +// GetCPUInfo 获取cpu信息 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 1:24 下午 2021/11/27 +func GetCPUInfo(client *redis.Client) (*CPU, error) { + var result CPU + if err := infoToStruct(client, "cpu", &result); nil != err { + return nil, err + } + return &result, nil +} + +// GetCommandStats 命令状态 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 1:27 下午 2021/11/27 +func GetCommandStats(client *redis.Client) (*Stats, error) { + var result Stats + if err := infoToStruct(client, "stats", &result); nil != err { + return nil, err + } + return &result, nil +} + +// GetCommandInfo 获取命令信息 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 1:32 下午 2021/11/27 +func GetCommandInfo(client *redis.Client) ([]CmdStat, error) { + var result map[string]string + if err := infoToStruct(client, "commandstats", &result); nil != err { + return make([]CmdStat, 0), err + } + cmdList := make([]CmdStat, 0) + // 解析数据 + for cmd, detail := range result { + infoArr := strings.Split(detail, ",") + if len(infoArr) < 2 { + // 去掉开始的描述信息 + continue + } + info := CmdStat{ + Cmd: strings.ReplaceAll(strings.ToLower(cmd), "cmdstat_", ""), + Calls: 0, + TotalUsedTime: 0, + AvgUsedTime: 0, + RejectedCalls: 0, + FailedCalls: 0, + SuccessCalls: 0, + } + for _, item := range infoArr { + itemArr := strings.Split(item, "=") + if len(itemArr) != 2 { + continue + } + switch strings.ToLower(itemArr[0]) { + case "calls": + _ = convert.ConvertAssign(&info.Calls, itemArr[1]) + case "usec": + _ = convert.ConvertAssign(&info.TotalUsedTime, itemArr[1]) + case "usec_per_call": + _ = convert.ConvertAssign(&info.AvgUsedTime, itemArr[1]) + case "rejected_calls": + _ = convert.ConvertAssign(&info.RejectedCalls, itemArr[1]) + case "failed_calls": + _ = convert.ConvertAssign(&info.FailedCalls, itemArr[1]) + } + } + info.SuccessCalls = info.Calls - info.FailedCalls - info.RejectedCalls + cmdList = append(cmdList, info) + } + return cmdList, nil +} + +// GetKeyspace 获取 keyspace 信息 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2:44 下午 2021/11/27 +func GetKeyspace(client *redis.Client) ([]DB, error) { + var result map[string]string + if err := infoToStruct(client, "keyspace", &result); nil != err { + return make([]DB, 0), err + } + dbList := make([]DB, 0) + for dbName, item := range result { + itemArr := strings.Split(item, ",") + if len(itemArr) < 3 { + continue + } + dbInfo := DB{ + Name: dbName, + Keys: 0, + Expires: 0, + AvgTTL: 0, + } + for _, kv := range itemArr { + kvArr := strings.Split(kv, "=") + if len(kvArr) != 2 { + continue + } + switch strings.ToLower(kvArr[0]) { + case "keys": + _ = convert.ConvertAssign(&dbInfo.Keys, kvArr[1]) + case "expires": + _ = convert.ConvertAssign(&dbInfo.Expires, kvArr[1]) + case "avg_ttl": + _ = convert.ConvertAssign(&dbInfo.AvgTTL, kvArr[1]) + } + } + dbList = append(dbList, dbInfo) + } + return dbList, nil +} + +// GetCluster 获取 cluster 信息 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 3:30 下午 2021/11/27 +func GetCluster(client *redis.Client) (*Cluster, error) { + var result Cluster + if err := infoToStruct(client, "cluster", &result); nil != err { + return nil, err + } + return &result, nil +} + +// GetErrorStats 获取错误状态信息 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 3:35 下午 2021/11/27 +func GetErrorStats(client *redis.Client) ([]Error, error) { + var result map[string]string + if err := infoToStruct(client, "errorstats", &result); nil != err { + return make([]Error, 0), err + } + + errList := make([]Error, 0) + for errType, item := range result { + itemArr := strings.Split(item, "=") + if len(itemArr) != 2 { + continue + } + errInfo := Error{ + Type: strings.ReplaceAll(errType, "errorstat_", ""), + Count: 0, + } + _ = convert.ConvertAssign(&errInfo.Count, itemArr[1]) + errList = append(errList, errInfo) + } + return errList, nil +} + +// infoToStruct 读取到的数据,解析到结构体 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 12:28 下午 2021/11/27 +func infoToStruct(client *redis.Client, module string, receiver interface{}) error { + data := client.Info(context.Background(), module).String() + // yaml 文件规范要求, key: val , 注意 : 的后面有空格 + data = strings.ReplaceAll(data, ":", ": ") + return yml.Unmarshal([]byte(data), receiver) +} diff --git a/middleware/redis/redis_test.go b/middleware/redis/redis_test.go index 2597543..06bc115 100644 --- a/middleware/redis/redis_test.go +++ b/middleware/redis/redis_test.go @@ -42,6 +42,12 @@ func TestCommandProxy(t *testing.T) { panic(err.Error()) } r, cmdErr := instance.CommandProxy(nil, "test_redis", "set", "command_proxy", "hello world") + c, _ := instance.GetRedisClient("test_redis") + fmt.Println(GetRedisServerInfo(c.Instance)) + fmt.Println(GetCommandInfo(c.Instance)) + fmt.Println(GetKeyspace(c.Instance)) + fmt.Println(GetCluster(c.Instance)) + fmt.Println(GetErrorStats(c.Instance)) assert.Nil(t, cmdErr, "命令执行成功") assert.Equal(t, "OK", fmt.Sprintf("%v", r)) }