package
0.0.0-20250122034743-c126e93bce09
Repository: https://github.com/walleframe/pb_plugins.git
Documentation: pkg.go.dev

# README

#+startup: content #+title: redis插件文档

  • protoc-gen-redis

通过定义一个protobuf message. 来生成对应的mysql操作管理代码(避免使用反射).

生成代码使用的redis库是: github.com/redis/go-redis/v9


约定:

  • 必须在消息级定义 redis.key option才会生成redis代码.
  • 生成redis操作的代码和操作的结构体不在一个包内.
  • 同一个消息/key 只能定义一种数据类型,防止误操作.

protoc-gen-redis 有两种使用方式.

  • 默认将消息体本身作为操作对象,每种redis类型各有差异.
  • 定义的消息体本身只用来解析,用于生成redis操作.

之所以区分两种使用方式,主要是为了避免生成不必要的protobuf文件.

如果将操作的对象结构体放在a.proto内, op.proto定义redis行为,那么op.proto可以不生成pb文件.

第二种方式通过使用环境变量 REDIS_OP_ANOTHRER 或者命令行参数 op_another 来指定默认行为.

全局指定消息的序列化方法通过设置全局变量 REDIS_MSG_PROTOCOL=proto 或者命令行参数 msg_protobuf=proto 来设置.

默认支持2种序列化方式.

  • proto => google protobuf message
  • json

单独某个消息可以使用 redis.json=true , redis.pb=true 来指定特定的序列化方式.

自定义序列化和反序列化可以自己写一个名为proto的包,提供Marshal和Unmashal,以及Message接口定义. 通过 REDIS_PKG_PB 或者 pkg_protobuf 来修改包路径


** 基础配置信息 | 描述 | 环境变量 | 命令行参数 | 默认值 | |--------------------+--------------------+--------------+----------------------------------------| | 代码路径 | REDIS_OPCODE_PATH | code_path | pkg/gen/redisop | | 服务管理包 | REDIS_PKG_SVC | pkg_svc | github.com/walleframe/svc_redis | | 工具包 | REDIS_PKG_UTIL | pkg_util | github.com/walleframe/walle/util | | | REDIS_PKG_PB | pkg_protobuf | google.golang.org/protobuf/proto | | | REDIS_PKG_TIME | pkg_time | github.com/walleframe/walle/util/wtime | | | REDIS_PKG_JSON | pkg_json | encoding/json | | 默认行为 | REDIS_OP_ANOTHRER | op_another | false | | 全局消息序列化方法 | REDIS_MSG_PROTOCOL | msg_protocol | proto | | 检查lua语法 | REDIS_CHECK_LUA | check_lua | true |

uitl包

  • 主要使用 util.Buffer 来进行字符串格式化(为了在运行期间尽量少的申请内存).

svc管理包

  • 使用 RawTo... , AnyFrom... 等方法简化生成代码逻辑.
  • 使用 RegisterDB 等管理数据库.

配置优先级:

  • 命令行参数
  • 环境变量
  • 默认值

REDIS_CHECK_LUA / check_lua 默认为true, 只检查最基础的lua语法.

** 选项配置 文件级选项: | options | type | default | desc | |------------+--------+-----------+-----------------------------| | op_package | string | file name | 指定生成包名,默认使用文件名 |

以下全是消息级option定义. | options | type | default | desc | |-------------------------+--------+-----------------+-----------------------------------| | redis.key | string | | key 定义 | | redis.size | int | 64 | key 预估大小,默认64 | | redis.type | string | | redis对应结构体类型 | | redis.op_field | bool | false | 是否不操作消息体本身,而是操作字段 | | redis.field/redis.value | string | | 详情见 hash/set/zset类型 | | redis.member | string | string | set/zset用来定义member拼接 | | redis.script | struct | RedisScript数组 | 详情见 redis script | | redis.json | bool | false | 指定使用json格式序列化 | | redis.pb | bool | false | 指定使用二进制序列化 |

redis.type 定义的对应的redis数据类型. string/hash/zset/set/lock 互斥. '!' 开头屏蔽redis keys 相关操作生成. 例: redis.key="!string"

#+begin_src protobuf message RedisScript { string name = 1; string lua = 2; string argv = 3; string reply = 4; } #+end_src

** key 定义 默认使用 : 分隔key的每一个部分. 每个部分的字符必须是 a-z A-Z 0-9.

例: userdata:123

*** 时间戳 @time-func[op] 使用 @ 开头,后接获取时间戳函数.可以对时间进行 + / - / % 操作.

支持的函数有: | function | type | func | desc | |------------+--------+-------------------------+----------------------------------------------------| | daystamp | int64 | wtime.DayStamp() | current day 00:00:00 | | weekstamp | int64 | wtime.WeekStamp() | current week first day 00:00:00 | | monthstamp | int64 | wtime.MonthStamp() | current month 1st 00:00:00 | | yearstamp | int64 | wtime.YearStamp() | current year January 1st 00:00:00 | | curday | string | wtime.CurDayString() | current day, format '20060102' | | nextday | string | wtime.NextDayString() | next day, format '20060102' | | curweek | string | wtime.CurWeekString() | current week first day 00:00:00, format '20060102' | | nextweek | string | wtime.NextWeekString() | next week first day 00:00:00, format '20060102' | | curmonth | string | wtime.CurMonthString() | current month 1st,format '200601' | | nextmonth | string | wtime.NextMonthString() | next month 1st,format '200601' | | curyear | string | wTime.CurYearString() | | | nextyear | string | wtime.NextYearString() | |

例: #+begin_example u:login:@daystamp

u:check:@month+3600:xx

xx:@weestamp-1800:xx #+end_example

*** 外部输入 go类型参数 $[arg-name=]go-type[op] 使用 $ 开头,可选的设置参数名称(影响生成代码).可以对数值进行 + / - / % 操作. 支持go基础类型: int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64, bool, string

例: #+begin_example xx:$uint64:xxx

xx:$uid=int64:xxx

xx:$name=string:xx #+end_example

** 基础接口和key相关操作接口

*** 基础接口 通过pb消息生成对应接口, 使用时直接引用对应包名及PB消息名即可.

#+begin_src protobuf // player_info.proto message PlayerInfo { option (redis.type) = "string"; option (redis.key) = "u:info:$uid=int64"; option (redis.size) = 32;

int64  id      = 1;
string name    = 2;
int32  level   = 3;
int32  exp     = 4;
int32  gold    = 5;
int32  diamond = 6;
int32  vip     = 7;

} #+end_src 会生成对应接口 #+begin_src go func PlayerInfo(uid int64) *xPlayerInfo // With reset redis client func (x *xPlayerInfo) With(rds redis.UniversalClient) *xPlayerInfo

func (x *xPlayerInfo) Key() string #+end_src

使用: #+begin_src go func Xxxx() { ok, err := player_info.PlayerInfo(123).Del(ctx) // .... } #+end_src

*** key接口 在类型前使用 ! 来禁止生成key接口. key接口如下: #+begin_src go Del(ctx context.Context) (ok bool, err error) Exists(ctx context.Context) (ok bool, err error) Expire(ctx context.Context, expire time.Duration) (ok bool, err error) ExpireNX(ctx context.Context, expire time.Duration) (ok bool, err error) ExpireXX(ctx context.Context, expire time.Duration) (ok bool, err error) ExpireGT(ctx context.Context, expire time.Duration) (ok bool, err error) ExpireLT(ctx context.Context, expire time.Duration) (ok bool, err error) ExpireAt(ctx context.Context, expire time.Time) (ok bool, err error) TTL(ctx context.Context) (time.Duration, error) PExpire(ctx context.Context, expire time.Duration) (ok bool, err error) PExpireAt(ctx context.Context, expire time.Time) (ok bool, err error) PExpireTime(ctx context.Context) (time.Duration, error) PTTL(ctx context.Context) (time.Duration, error) Persist(ctx context.Context) (ok bool, err error) Rename(ctx context.Context, newKey string) (err error) RenameNX(ctx context.Context, newKey string) (ok bool, err error) Type(ctx context.Context) (string, error) #+end_src ** redis-string redis.type = "string"

*** 未设置 REDIS_OP_ANOTHRER 时 默认将消息体本身作为序列化对象. 序列化方式请根据需要指定全局方式或者单独指定格式. 例如想单独使用json序列化,请设置 redis.json=true.

例: #+begin_src protobuf message PlayerInfoJson { option (redis.type) = "!string"; option (redis.key) = "u:info:$uid=int64"; option (redis.size) = 32; option (redis.json) = true;

int64  id      = 1;
string name    = 2;
int32  level   = 3;
int32  exp     = 4;

} #+end_src 对应redis结构 #+begin_src shell redis-cli get get u:info:11 => uid=11 "{"id":11,"name":"x","level":1,"exp":1}" #+end_src 生成相关接口如下: #+begin_src go func (x *xPlayerInfoJson) Set(ctx context.Context, msg *redisop.PlayerInfoJson, expire time.Duration) error func (x *xPlayerInfoJson) SetNX(ctx context.Context, msg *redisop.PlayerInfoJson, expire time.Duration) error func (x *xPlayerInfoJson) SetEx(ctx context.Context, msg *redisop.PlayerInfoJson, expire time.Duration) error func (x *xPlayerInfoJson) Get(ctx context.Context, msg *redisop.PlayerInfoJson) error #+end_src *** 设置 REDIS_OP_ANOTHRER 或者 redis.op_field=true

**** 空消息体,没有字段.生成通用消息接口.

生成消息通用接口. 即操作对象是 proto.Message 或者 json

通用消息接口 请慎用 . 同一个key保存不同数据肯定会产生异常.

pb接口示例: #+begin_src go Set(ctx context.Context, pb proto.Message, expire time.Duration) error SetNX(ctx context.Context, pb proto.Message, expire time.Duration) error SetEx(ctx context.Context, pb proto.Message, expire time.Duration) error Get(ctx context.Context, pb proto.Message) error #+end_src json接口示例: #+begin_src go Set(ctx context.Context, msg any, expire time.Duration) error SetNX(ctx context.Context, msg any, expire time.Duration) error SetEx(ctx context.Context, msg any, expire time.Duration) error Get(ctx context.Context, msg any) error #+end_src

**** 1个字段的结构体. 生成直接操作这个字段类型的接口

  • 不支持 bool/[]byte 类型

  • 数值类型,浮点类型,string类型,将生成对应类型get/set接口 例: #+begin_src protobuf message RedisStringOpInt32 { option (redis.type) = "string"; option (redis.key) = "u:int32:$uid=int64"; option (redis.size) = 32; option (redis.op_field) = true;

    int32 value = 1; } #+end_src 对应redis结构 #+begin_src shell redis-cli get u:int32:1 => uid=1 "1" => value=1 #+end_src 数值类型会生成对应的incr等原子性操作方法. #+begin_src go Incr(ctx context.Context) (int32, error) IncrBy(ctx context.Context, val int) (_ int32, err error) Decr(ctx context.Context) (int32, error) DecrBy(ctx context.Context, val int) (_ int32, err error) Get(ctx context.Context) (int32, error) Set(ctx context.Context, val int32, expire time.Duration) error SetNX(ctx context.Context, val int32, expire time.Duration) (bool, error) SetEx(ctx context.Context, val int32, expire time.Duration) error #+end_src

  • 自定义类型 生成操作指定类型的接口 同未设置 REDIS_OP_ANOTHRER 时候,只是操作的是字段类型的结构体. 示例: #+begin_src go Set(ctx context.Context, pb *redisop.Object, expire time.Duration) error SetNX(ctx context.Context, pb *redisop.Object, expire time.Duration) error SetEx(ctx context.Context, pb *redisop.Object, expire time.Duration) error Get(ctx context.Context, pb *redisop.Object) error #+end_src **** 2个及2个以上的字段, 不支持 ** redis-hash redis.type = "hash"

redis.field/redis.value 用来指定如何拼接 redis hash的field,和value值. 规则参考key定义中的外部输入go类型.

注意: 拼接key,value不会生成返回map结构的接口.

例: #+begin_src protobuf message RedisHashOp { option (redis.type) = "hash"; option (redis.key) = "x:hash:$xx=int64"; option (redis.size) = 32; option (redis.field) = "$uid=int64:$sex=int8:$level=uint8"; option (redis.value) = "$abc=uint8:$def=uint8"; } #+end_src 对应redis结构 #+begin_src shell redis-cli hgetall x:hash:11 => xx=11

  1. "1:2:1" => uid=1 sex=2 level=1
  2. "1:2" => abc=1 def=2
  3. "2:3:2" => uid=2 sex=3 level=2
  4. "2:5" => abc=2 def=5 #+end_src

同时设置 redis.field/redis.value 后直接按照拼接规则生成.忽略其他选项(只要有拼接数据存在,就不会生成返回map结构的接口.)

接口示例:

// 拼接field和value相关接口. 包级函数. 例: player_info.MergeRedisHashOpField(....) #+begin_src go func MergeRedisHashOpField(uid int64, sex int8, level uint8) string func SplitRedisHashOpField(val string) (uid int64, sex int8, level uint8, err error) func MergeRedisHashOpKVValue(abc uint8, def uint8) string func SplitRedisHashOpKVValue(val string) (abc uint8, def uint8, err error) #+end_src

操作接口示例: #+begin_src go GetField(ctx context.Context, uid int64, sex int8, level uint8) (abc uint8, def uint8, err error) SetField(ctx context.Context, uid int64, sex int8, level uint8, abc uint8, def uint8) (err error) HKeysRange(ctx context.Context, filter func(uid int64, sex int8, level uint8) bool) (err error) HValsRange(ctx context.Context, filter func(abc uint8, def uint8) bool) (err error) HExists(ctx context.Context, uid int64, sex int8, level uint8) (bool, error) HDel(ctx context.Context, uid int64, sex int8, level uint8) (bool, error) HLen(ctx context.Context) (count int64, err error) HRandFieldRange(ctx context.Context, count int, filter func(uid int64, sex int8, level uint8) bool) (err error) HRandFieldWithValuesRange(ctx context.Context, count int, filter func(uid int64, sex int8, level uint8, abc uint8, def uint8) bool) (err error) HScanRange(ctx context.Context, match string, count int, filter func(uid int64, sex int8, level uint8, abc uint8, def uint8) bool) (err error) #+end_src *** 未设置 REDIS_OP_ANOTHRER 时 将结构体展开,每个字段 字段名对应一个field, 字段的值是value. 数值类型字段会生成incr方法.

例: #+begin_src protobuf message RedisHashOpMessage { option (redis.type) = "hash"; option (redis.key) = "u:hash:$uid=int64"; option (redis.size) = 32;

int32  level = 1;
string name  = 2;

} #+end_src

对应redis结构 #+begin_src shell redis-cli hgetall u:hash:111 => uid=111

  1. "level"
  2. "10"
  3. "name"
  4. "xx" #+end_src

接口示例: #+begin_src go SetRedisHashOpMessage(ctx context.Context, obj *redisop.RedisHashOpMessage) (err error) GetRedisHashOpMessage(ctx context.Context) (*redisop.RedisHashOpMessage, error) MGetRedisHashOpMessage(ctx context.Context) (*redisop.RedisHashOpMessage, error) GetLevel(ctx context.Context) (_ int32, err error) SetLevel(ctx context.Context, val int32) (err error) IncrByLevel(ctx context.Context, incr int) (int32, error) GetName(ctx context.Context) (_ string, err error) SetName(ctx context.Context, val string) (err error) #+end_src

*** 设置 REDIS_OP_ANOTHRER 或者 redis.op_field=true **** 空消息体,没有字段. 不支持 **** 1个字段的结构体.

  • 未设置 redis.field/redis.value ,仅支持消息类型. 将自定义类型展开,自定义类型的字段必须都是基础数据类型. 参考 未设置 REDIS_OP_ANOTHRER .

  • 设置了 redis.field/redis.value 其中一个, 字段就是另外一个值. 只要有拼接数据存在,就不会生成返回map结构的接口.

    比如设置了 redis.field ,结构体中的字段就是 hash结构的value类型.

*** 2个字段的结构体.

  • 第一个字段 定义field类型,第二个字段 定义value类型. 支持scan操作.
  • field 类型仅支持基础类型 如果是float,bytes,不会生成返回map结构体接口.
  • value 类型支持基础数据类型和自定义结构体类型.

*** +3个字段结构体+ 不支持 +第一个字段结构体, 第二三个字段配置 动态字段类型和动态value类型.+ +NOTE: 不生成hgetall接口+

需要调整hvals,hfields,range等接口实现,有实际需求再弄.

** redis-set redis.type = "set" redis.member 用来定义如何拼接一个string作为set结构体的member. 规则参考key定义中的外部输入go类型.

设置此选项后将忽略其他配置选项.

例: #+begin_src protobuf message RedisSetOpMember { option (redis.type) = "set"; option (redis.key) = "u:set:$xx=int64"; option (redis.size) = 32; option (redis.member) = "$uid=int64:$sex=int8:$level=uint8"; } #+end_src 对应redis结构 #+begin_src shell redis-cli smembers u:set:1 => xx=1

  1. "1:1:1" => uid=1 sex=1 level=1
  2. "2:2:2" => uid=2 sex=2 level=2 #+end_src

*** 未设置 REDIS_OP_ANOTHRER 时 默认将消息体本身作为序列化对象.

*** 设置 REDIS_OP_ANOTHRER 或者 redis.op_field=true **** 空消息体,没有字段. 通用消息接口 **** 1个字段的结构体

  • 不支持 bool 类型
  • 未生成集合类型相关接口(有需要再加)
  • 自定义类型 以当前字段的结构体类型作为序列化对象. **** 2个及2个以上的字段, 不支持

** redis-zset redis.type = "set" member 不支持 bool 类型,数组类型和map类型 ,score 仅支持有符号的数值和float类型.

*** 未设置 REDIS_OP_ANOTHRER 并且未设置 redis.op_field=true 将结构体本身作为序列化对象, score默认float64

*** 设置 REDIS_OP_ANOTHRER 或者 redis.op_field=true

**** 设置 redis.member ***** 空消息体,没有字段. score默认是float64 ***** 1个字段. 必须是有符号类型数值. 或者float/double ***** 2哥字段及以上, 不支持 **** 未设置 redis.member ***** 空消息体,没有字段. 不支持 ***** 1个字段.

  • filed 1 是member
  • score 默认是 float64 类型 ***** 2个字段
  • filed 1 是member
  • field 2 是score. ***** 3个及3个以上的字段, 不支持 ** redis脚本 只支持生成对一个key进行操作的redis script相关代码. 同一个key(message)定义,可以添加多个脚本.

定义一个redis script,必须设置3个选项:

  1. name 脚本名,同一个消息体内必须不能相同.
  2. lua 定义实际lua脚本数据.
  3. argv 定义脚本参数. 规则参考 redis.field.
  4. reply 定义脚本返回值. 规则参考 redis.field.

例如定义名为 test 的脚本. #+begin_src protobuf option (redis.script) = { name: "test", lua: "redis.call('set', KEYS[1], ARGV[1]) return redis.call('get', KEYS[1])", argv: "$uid=int64", reply: "$v=int64", }; #+end_src

脚本参数和脚本返回值必须设置不冲突的名称.

#+begin_src go Test(ctx context.Context, uid int64) (_ int64, err error) #+end_src

** lock 分布式锁

redis.type = "lock"

#+begin_src go func (x *xXXXLock) Lock(ctx context.Context, expiration time.Duration) (lockID string, err error) func (x *xXXXLock) UnLock(ctx context.Context, lockID string) (ok bool, err error) func (x *xXXXLock) LockFunc(ctx context.Context, expiration time.Duration) (unlock func(ctx context.Context), err error) #+end_src

** TODO: redis-bitmap 有需求再弄.

** TODO: redis-zset 保存的score有效位数最多53位. 根据不同业务场景定制score. 划分53位来精确保存数值.

** NOTE: redis list/pubsub/stream/zpop 不封装在这个生成里面,之后会封成pipeline接口.

# Variables

No description provided by the author