feature/redis #9
326
middleware/redis/monitor.go
Normal file
326
middleware/redis/monitor.go
Normal file
@ -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)
|
||||||
|
}
|
@ -42,6 +42,12 @@ func TestCommandProxy(t *testing.T) {
|
|||||||
panic(err.Error())
|
panic(err.Error())
|
||||||
}
|
}
|
||||||
r, cmdErr := instance.CommandProxy(nil, "test_redis", "set", "command_proxy", "hello world")
|
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.Nil(t, cmdErr, "命令执行成功")
|
||||||
assert.Equal(t, "OK", fmt.Sprintf("%v", r))
|
assert.Equal(t, "OK", fmt.Sprintf("%v", r))
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user