用go设计开发一个自己的轻量级登录库/框架吧(拓展篇),给自己的库/框架拓展一下吧,主库:https://github.com/weloe/token-go
主库:weloe/token-go: a light login library.
扩展库:weloe/token-go-extensions (github.com)
本篇给主库扩展一个Adapter提供简单的外部数据存储。
一个库/框架往往不能完成所有事情,需要其他库/框架的支持才能达到更加完善的效果。本篇会对token-go框架的Adapter进行简单的拓展。
首先我们应该想想Adapter是用来干什么的?
从第一篇我们就明确其职责,就是存储数据。我们在token-go里提供了一个内置的adapter:default_adapter ,用于框架底层的数据存储,但是这种内存的数据存储有着很多的缺陷,并且没有经过实际的生产测试使用。也因此,我们应该提供更成熟的存储方案来提供给使用者去替代它。
这就是本篇要实现的redis_adapter了
这里还有一个点要注意,将数据存储到外部需要确定数据的序列化和反序列化方法。因此,我们加了一个SerializerAdapter接口,要求新的Adapter选择实现。
token-go/serializer_adapter.go at master · weloe/token-go · GitHub
具体的调用则是在enforcer对session进行存储或者取出数据的时候进行调用。
这里的sessionUnSerialize()
实际上就是尝试调用了adapter实现的反序列化方法。同理SetSession()
也是一样的。
最后就是RedisAdapter了
token-go-extensions/adapter.go at master · weloe/token-go-extensions · GitHub
并不难,只要实现我们之前的Adapter和SerializerAdapter两个接口就行了。
序列化方法使用json,方便查看
package redis_adapter
import (
"context"
"encoding/json"
"github.com/go-redis/redis/v8"
"github.com/weloe/token-go/model"
"github.com/weloe/token-go/persist"
"time"
)
var _ persist.Adapter = (*RedisAdapter)(nil )
var _ persist.SerializerAdapter = (*RedisAdapter)(nil )
type RedisAdapter struct {
client *redis.Client
}
func (r *RedisAdapter) Serialize(session *model.Session) ([]byte , error ) {
return json.Marshal(session)
}
func (r *RedisAdapter) UnSerialize(bytes []byte ) (*model.Session, error ) {
s := &model.Session{}
err := json.Unmarshal(bytes, s)
if err != nil {
return nil , err
}
return s, nil
}
func (r *RedisAdapter) GetStr(key string ) string {
res, err := r.client.Get(context.Background(), key).Result()
if err != nil {
return ""
}
return res
}
func (r *RedisAdapter) SetStr(key string , value string , timeout int64 ) error {
err := r.client.Set(context.Background(), key, value, time.Duration(timeout)*time.Second).Err()
if err != nil {
return err
}
return nil
}
func (r *RedisAdapter) UpdateStr(key string , value string ) error {
err := r.client.Set(context.Background(), key, value, 0 ).Err()
if err != nil {
return err
}
return nil
}
func (r *RedisAdapter) DeleteStr(key string ) error {
err := r.client.Del(context.Background(), key).Err()
if err != nil {
return err
}
return nil
}
func (r *RedisAdapter) GetStrTimeout(key string ) int64 {
duration, err := r.client.TTL(context.Background(), key).Result()
if err != nil {
return -1
}
return int64 (duration.Seconds())
}
func (r *RedisAdapter) UpdateStrTimeout(key string , timeout int64 ) error {
var duration time.Duration
if timeout < 0 {
duration = -1
} else {
duration = time.Duration(timeout) * time.Second
}
err := r.client.Expire(context.Background(), key, duration).Err()
if err != nil {
return err
}
return nil
}
func (r *RedisAdapter) Get(key string ) interface {} {
res, err := r.client.Get(context.Background(), key).Result()
if err != nil {
return nil
}
s := &model.Session{}
err = json.Unmarshal([]byte (res), s)
if err != nil {
return nil
}
return s
}
func (r *RedisAdapter) Set(key string , value interface {}, timeout int64 ) error {
err := r.client.Set(context.Background(), key, value, time.Duration(timeout)*time.Second).Err()
if err != nil {
return err
}
return nil
}
func (r *RedisAdapter) Update(key string , value interface {}) error {
err := r.client.Set(context.Background(), key, value, 0 ).Err()
if err != nil {
return err
}
return nil
}
func (r *RedisAdapter) Delete(key string ) error {
err := r.client.Del(context.Background(), key).Err()
if err != nil {
return err
}
return nil
}
func (r *RedisAdapter) GetTimeout(key string ) int64 {
duration, err := r.client.TTL(context.Background(), key).Result()
if err != nil {
return -1
}
return int64 (duration.Seconds())
}
func (r *RedisAdapter) UpdateTimeout(key string , timeout int64 ) error {
var duration time.Duration
if timeout < 0 {
duration = -1
} else {
duration = time.Duration(timeout) * time.Second
}
err := r.client.Expire(context.Background(), key, duration).Err()
if err != nil {
return err
}
return nil
}
func (r *RedisAdapter) DeleteBatchFilteredKey(filterKeyPrefix string ) error {
var cursor uint64 = 0
for {
keys, cursor, err := r.client.Scan(context.Background(), cursor, filterKeyPrefix+"*" , 100 ).Result()
if err != nil {
return err
}
if len (keys) == 0 && cursor == 0 {
break
}
pipe := r.client.Pipeline()
for _, key := range keys {
pipe.Del(context.Background(), key)
}
_, err = pipe.Exec(context.Background())
if err != nil {
return err
}
}
return nil
}
实现完接口,再写几个初始化方法
就不贴测试代码了,就放个链接~
token-go-extensions/adapter_test.go at master · weloe/token-go-extensions · GitHub
这样RedisAdapter就开发完了吗?不不不,并没有。
用户量的增大,对容错,一致性等等的要求提高,可能需要用到多个redis,这就需要我们继续适配开发一个ClusterAdapter了,为什么我这里不往下写了?阳了好累 当然是因为还在开发中~~
__EOF__
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 我与微信审核的“相爱相杀”看个人小程序副业