增加 redis client

This commit is contained in:
2021-02-27 22:09:31 +08:00
parent 22ec5e9893
commit 3411acc73d
6 changed files with 536 additions and 0 deletions

217
middleware/redis/clent.go Normal file
View File

@ -0,0 +1,217 @@
// Package redis ...
//
// Description : redis 客户端
//
// Author : go_developer@163.com<张德满>
//
// Date : 2021-02-27 4:49 下午
package redis
import (
"time"
"github.com/go-developer/gopkg/convert"
"github.com/go-developer/gopkg/logger"
redisInstance "github.com/go-redis/redis/v8"
"go.uber.org/zap"
)
// Options 连接选项,百分之百兼容第三方包的选项
//
// Author : go_developer@163.com<张德满>
//
// Date : 4:57 下午 2021/2/27
type Options struct {
Conf *redisInstance.Options // 第三方包的选项
Logger *LoggerConfig // 日志的配置
LoggerFieldConfig *LogFieldConfig // 日志字段的配置
}
// RealClient 包装好的 redis client
type RealClient struct {
Flag string // redis 标识
Instance *redisInstance.Client // redis 实例
Logger *zap.Logger // 日志实例
LoggerFieldConfig *LogFieldConfig // 日志字段的配置
}
// NewClient 获取redis client实例
//
// Author : go_developer@163.com<张德满>
//
// Date : 5:05 下午 2021/2/27
func NewClient(config map[string]Options) (*Client, error) {
c := &Client{
instanceTable: make(map[string]*RealClient),
confTable: config,
}
return c, c.init()
}
// Client 包装的redis client
//
// Author : go_developer@163.com<张德满>
//
// Date : 4:52 下午 2021/2/27
type Client struct {
instanceTable map[string]*RealClient // redis 实例
confTable map[string]Options // redis 配置
}
// init 初始化redis连接
//
// Author : go_developer@163.com<张德满>
//
// Date : 5:31 下午 2021/2/27
func (c *Client) init() error {
var (
err error
)
for flag, conf := range c.confTable {
c.instanceTable[flag] = &RealClient{
Flag: flag,
Instance: redisInstance.NewClient(conf.Conf),
Logger: nil,
LoggerFieldConfig: conf.LoggerFieldConfig,
}
if c.instanceTable[flag].Logger, err = c.getLogger(conf.Logger); nil != err {
return LoggerInitFail(flag, err)
}
if nil == c.instanceTable[flag].LoggerFieldConfig {
c.instanceTable[flag].LoggerFieldConfig = &LogFieldConfig{
Message: "",
UsedTimeField: "",
CommandField: "",
FlagField: "",
}
}
if len(c.instanceTable[flag].LoggerFieldConfig.Message) == 0 {
c.instanceTable[flag].LoggerFieldConfig.Message = defaultMessage
}
if len(c.instanceTable[flag].LoggerFieldConfig.CommandField) == 0 {
c.instanceTable[flag].LoggerFieldConfig.CommandField = defaultCommandField
}
if len(c.instanceTable[flag].LoggerFieldConfig.UsedTimeField) == 9 {
c.instanceTable[flag].LoggerFieldConfig.UsedTimeField = defaultUsedTimeField
}
if len(c.instanceTable[flag].LoggerFieldConfig.FlagField) == 0 {
c.instanceTable[flag].LoggerFieldConfig.FlagField = defaultFlagField
}
}
return nil
}
// getLogger ...
//
// Author : go_developer@163.com<张德满>
//
// Date : 7:07 下午 2021/2/27
func (c *Client) getLogger(conf *LoggerConfig) (*zap.Logger, error) {
if nil == conf || nil == conf.SplitConfig {
return nil, nil
}
return logger.NewLogger(
conf.LoggerLevel,
conf.ConsoleOutput,
conf.Encoder,
conf.SplitConfig,
)
}
// GetRedisClient 获取redis实例
//
// Author : go_developer@163.com<张德满>
//
// Date : 5:16 下午 2021/2/27
func (c *Client) GetRedisClient(flag string) (*RealClient, error) {
redisClient, exist := c.instanceTable[flag]
if !exist {
return nil, FlagNotFound(flag)
}
return redisClient, nil
}
// log 记录redis请求日志
//
// Author : go_developer@163.com<张德满>
//
// Date : 8:52 下午 2021/2/27
func (c *Client) log(ctx *Context, realClient *RealClient, cmdResult redisInstance.Cmder, startTime int64, finishTime int64) {
if nil == realClient || nil == realClient.Logger {
return
}
realClient.Logger.Info(
"执行redis命令日志记录",
zap.Any(ctx.RequestIDField, ctx.RequestID), // 上下文串联的requestID
zap.String(realClient.LoggerFieldConfig.CommandField, cmdResult.String()), // 执行的命令
zap.Float64(realClient.LoggerFieldConfig.UsedTimeField, float64(finishTime-startTime)/1e6), // 耗时,单位: ms
zap.Error(cmdResult.Err()), // 异常信息
)
}
// CommandProxy 执行命令的代理
//
// Author : go_developer@163.com<张德满>
//
// Date : 9:41 下午 2021/2/27
func (c *Client) CommandProxy(ctx *Context, flag string, cmd string, param ...interface{}) (interface{}, error) {
var (
realClient *RealClient
err error
)
if len(cmd) == 0 {
return nil, EmptyCmd()
}
if realClient, err = c.GetRedisClient(ctx.Flag); nil != err {
return nil, err
}
redisCmd := append([]interface{}{cmd}, param...)
startTime := time.Now().Unix()
cmdResult := realClient.Instance.Do(ctx.Ctx, redisCmd...)
go c.log(ctx, realClient, cmdResult, startTime, time.Now().UnixNano())
return cmdResult.Val(), cmdResult.Err()
}
// CommandProxyWithReceiver 执行命令,并解析结果
//
// Author : go_developer@163.com<张德满>
//
// Date : 10:00 下午 2021/2/27
func (c *Client) CommandProxyWithReceiver(ctx *Context, flag string, receiver interface{}, cmd string, param ...interface{}) error {
if nil == receiver {
return ReceiverISNIL()
}
var (
err error
result interface{}
)
if result, err = c.CommandProxy(ctx, flag, cmd, param); nil != err {
return err
}
return ResultConvertFail(convert.ConvertAssign(receiver, result))
}
// Set set 命令
//
// Author : go_developer@163.com<张德满>
//
// Date : 8:18 下午 2021/2/27
func (c *Client) Set(ctx *Context, key string, value interface{}, expiration time.Duration) error {
var (
realClient *RealClient
err error
statusCmd *redisInstance.StatusCmd
)
if realClient, err = c.GetRedisClient(ctx.Flag); nil != err {
return err
}
startTime := time.Now().UnixNano()
statusCmd = realClient.Instance.Set(ctx.Ctx, key, value, expiration)
go c.log(ctx, realClient, statusCmd, startTime, time.Now().UnixNano())
return statusCmd.Err()
}

141
middleware/redis/context.go Normal file
View File

@ -0,0 +1,141 @@
// Package redis...
//
// Description : redis...
//
// Author : go_developer@163.com<张德满>
//
// Date : 2021-02-27 8:22 下午
package redis
import (
"context"
"github.com/go-developer/gopkg/easymap"
"github.com/gin-gonic/gin"
)
const (
// 默认的 request_id 字段名
defaultRequestIDField = "request_id"
// 默认的message
defaultMessage = "执行redis命令日志记录"
// 耗时字段
defaultUsedTimeField = "used_field"
// 默认的命令字段
defaultCommandField = "command"
// 默认记录 redis标识的字段
defaultFlagField = "flag"
)
// Context 请求上下文
//
// Author : go_developer@163.com<张德满>
//
// Date : 8:25 下午 2021/2/27
type Context struct {
Flag string // 哪个模块的上下文
Ctx context.Context // ctx
GinCtx *gin.Context // http 请求绑定的gin.context
RequestIDField string // requestID 字段名
RequestID string // requestID 此字段有值, 直接使用此值,无值, 去GinCtx 中读取 RequestIDField
Extra easymap.EasyMap // 扩展信息
}
// NewContext 生成一个上下文
//
// Author : go_developer@163.com<张德满>
//
// Date : 8:26 下午 2021/2/27
func NewContext(flag string, of ...SetContextFunc) *Context {
ctx := &Context{
Flag: flag,
Ctx: nil,
GinCtx: nil,
RequestIDField: "",
RequestID: "",
Extra: nil,
}
for _, f := range of {
f(ctx)
}
if nil == ctx.Ctx {
ctx.Ctx = context.Background()
}
if len(ctx.RequestIDField) == 0 {
ctx.RequestIDField = defaultRequestIDField
}
if nil == ctx.Extra {
ctx.Extra = easymap.NewNormal(true)
}
// requestID 填充
if len(ctx.RequestID) == 0 {
// 先从 gin 读
if nil != ctx.Ctx {
ctx.RequestID = ctx.GinCtx.GetString(ctx.RequestIDField)
}
// 再从extra读取
if len(ctx.RequestID) == 0 {
ctx.RequestID, _ = ctx.Extra.GetString(ctx.RequestID)
}
}
return ctx
}
// SetContextFunc 设置context参数
type SetContextFunc func(rc *Context)
// WithCtx 设置context
//
// Author : go_developer@163.com<张德满>
//
// Date : 8:30 下午 2021/2/27
func WithCtx(ctx context.Context) SetContextFunc {
return func(rc *Context) {
rc.Ctx = ctx
}
}
// WithGinCtx 设置gin上下文
//
// Author : go_developer@163.com<张德满>
//
// Date : 8:34 下午 2021/2/27
func WithGinCtx(ginCtx *gin.Context) SetContextFunc {
return func(rc *Context) {
rc.GinCtx = ginCtx
}
}
// WithExtra 设置扩展信息
//
// Author : go_developer@163.com<张德满>
//
// Date : 8:36 下午 2021/2/27
func WithExtra(extra easymap.EasyMap) SetContextFunc {
return func(rc *Context) {
rc.Extra = extra
}
}
// WithRequestIDField 设置request_id参数名
//
// Author : go_developer@163.com<张德满>
//
// Date : 8:41 下午 2021/2/27
func WithRequestIDField(requestIDField string) SetContextFunc {
return func(rc *Context) {
rc.RequestIDField = requestIDField
}
}
// WithRequestID ...
//
// Author : go_developer@163.com<张德满>
//
// Date : 8:42 下午 2021/2/27
func WithRequestID(requestID string) SetContextFunc {
return func(rc *Context) {
rc.RequestID = requestID
}
}

64
middleware/redis/error.go Normal file
View File

@ -0,0 +1,64 @@
// Package redis...
//
// Description : redis...
//
// Author : go_developer@163.com<张德满>
//
// Date : 2021-02-27 5:13 下午
package redis
import "github.com/pkg/errors"
// FlagNotFound flag不存在异常
//
// Author : go_developer@163.com<张德满>
//
// Date : 5:15 下午 2021/2/27
func FlagNotFound(flag string) error {
return errors.Errorf("标识为 %s 的redis未找到", flag)
}
// LoggerInitFail 日志初始化失败
//
// Author : go_developer@163.com<张德满>
//
// Date : 7:30 下午 2021/2/27
func LoggerInitFail(flag string, err error) error {
return errors.Wrapf(err, "标识为 %s 的redis日志初始化失败", flag)
}
// EmptyCmd 未设置要执行的命令
//
// Author : go_developer@163.com<张德满>
//
// Date : 9:46 下午 2021/2/27
func EmptyCmd() error {
return errors.Errorf("未设置要执行的命令")
}
// CommandExecuteFail 命令执行失败
//
// Author : go_developer@163.com<张德满>
//
// Date : 9:58 下午 2021/2/27
func CommandExecuteFail(err error) error {
return errors.Wrapf(err, "命令执行异常")
}
// ReceiverISNIL 数据接收者是空指针
//
// Author : go_developer@163.com<张德满>
//
// Date : 10:05 下午 2021/2/27
func ReceiverISNIL() error {
return errors.Errorf("数据接收者指针为空")
}
// ResultConvertFail 数据结果解析失败
//
// Author : go_developer@163.com<张德满>
//
// Date : 10:07 下午 2021/2/27
func ResultConvertFail(err error) error {
return errors.Wrapf(err, "数据结果解析失败")
}

View File

@ -0,0 +1,39 @@
// Package redis...
//
// Description : redis...
//
// Author : go_developer@163.com<张德满>
//
// Date : 2021-02-27 5:26 下午
package redis
import (
"github.com/go-developer/gopkg/logger"
"go.uber.org/zap/zapcore"
)
// LoggerConfig 日志配置
//
// Author : go_developer@163.com<张德满>
//
// Date : 5:26 下午 2021/2/27
type LoggerConfig struct {
LoggerPath string
LoggerFile string
LoggerLevel zapcore.Level
ConsoleOutput bool
Encoder zapcore.Encoder
SplitConfig *logger.RotateLogConfig
}
// LogFieldConfig 日志字段配置
//
// Author : go_developer@163.com<张德满>
//
// Date : 9:20 下午 2021/2/27
type LogFieldConfig struct {
Message string
UsedTimeField string
CommandField string
FlagField string
}