用go设计开发一个自己的轻量级登录库/框架吧(拓展篇)

1|0给自己的库/框架拓展一下吧(拓展篇)

主库:weloe/token-go: a light login library.

扩展库:weloe/token-go-extensions (github.com)

本篇给主库扩展一个Adapter提供简单的外部数据存储。

2|0思路

一个库/框架往往不能完成所有事情,需要其他库/框架的支持才能达到更加完善的效果。本篇会对token-go框架的Adapter进行简单的拓展。

首先我们应该想想Adapter是用来干什么的?

从第一篇我们就明确其职责,就是存储数据。我们在token-go里提供了一个内置的adapter:default_adapter,用于框架底层的数据存储,但是这种内存的数据存储有着很多的缺陷,并且没有经过实际的生产测试使用。也因此,我们应该提供更成熟的存储方案来提供给使用者去替代它。

这就是本篇要实现的redis_adapter了

3|0实现

这里还有一个点要注意,将数据存储到外部需要确定数据的序列化和反序列化方法。因此,我们加了一个SerializerAdapter接口,要求新的Adapter选择实现。

token-go/serializer_adapter.go at master · weloe/token-go · GitHub

package persist import "github.com/weloe/token-go/model" type SerializerAdapter interface { Adapter Serialize(*model.Session) ([]byte, error) UnSerialize([]byte) (*model.Session, error) }

具体的调用则是在enforcer对session进行存储或者取出数据的时候进行调用。

func (e *Enforcer) GetSession(id string) *model.Session { if v := e.adapter.Get(e.spliceSessionKey(id)); v != nil { if s := e.sessionUnSerialize(v); s != nil { return s } else { session, ok := v.(*model.Session) if !ok { return nil } return session } } return nil }

这里的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 } // use pip delete batch 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 }

实现完接口,再写几个初始化方法

func NewAdapter(addr string, username string, password string, db int) (*RedisAdapter, error) { return NewAdapterByOptions(&redis.Options{ Addr: addr, Username: username, Password: password, DB: db, }) } func NewAdapterByOptions(options *redis.Options) (*RedisAdapter, error) { client := redis.NewClient(options) _, err := client.Ping(context.Background()).Result() if err != nil { return nil, err } return &RedisAdapter{client: client}, nil }

4|0测试

就不贴测试代码了,就放个链接~

token-go-extensions/adapter_test.go at master · weloe/token-go-extensions · GitHub

5|0最后

这样RedisAdapter就开发完了吗?不不不,并没有。

用户量的增大,对容错,一致性等等的要求提高,可能需要用到多个redis,这就需要我们继续适配开发一个ClusterAdapter了,为什么我这里不往下写了?阳了好累当然是因为还在开发中~~


__EOF__

本文作者秋玻
本文链接https://www.cnblogs.com/weloe/p/17418722.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   秋玻  阅读(404)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 我与微信审核的“相爱相杀”看个人小程序副业
点击右上角即可分享
微信分享提示