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

打印 上一主题 下一主题

主题 946|帖子 946|积分 2838

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

主库: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
  1. package persist
  2. import "github.com/weloe/token-go/model"
  3. type SerializerAdapter interface {
  4.         Adapter
  5.         Serialize(*model.Session) ([]byte, error)
  6.         UnSerialize([]byte) (*model.Session, error)
  7. }
复制代码
具体的调用则是在enforcer对session进行存储或者取出数据的时候进行调用。
  1. func (e *Enforcer) GetSession(id string) *model.Session {
  2.         if v := e.adapter.Get(e.spliceSessionKey(id)); v != nil {
  3.                 if s := e.sessionUnSerialize(v); s != nil {
  4.                         return s
  5.                 } else {
  6.                         session, ok := v.(*model.Session)
  7.                         if !ok {
  8.                                 return nil
  9.                         }
  10.                         return session
  11.                 }
  12.         }
  13.         return nil
  14. }
复制代码
这里的sessionUnSerialize()实际上就是尝试调用了adapter实现的反序列化方法。同理SetSession()也是一样的。
最后就是RedisAdapter了
token-go-extensions/adapter.go at master · weloe/token-go-extensions · GitHub
并不难,只要实现我们之前的Adapter和SerializerAdapter两个接口就行了。
序列化方法使用json,方便查看
  1. package redis_adapter
  2. import (
  3.         "context"
  4.         "encoding/json"
  5.         "github.com/go-redis/redis/v8"
  6.         "github.com/weloe/token-go/model"
  7.         "github.com/weloe/token-go/persist"
  8.         "time"
  9. )
  10. var _ persist.Adapter = (*RedisAdapter)(nil)
  11. var _ persist.SerializerAdapter = (*RedisAdapter)(nil)
  12. type RedisAdapter struct {
  13.         client *redis.Client
  14. }
  15. func (r *RedisAdapter) Serialize(session *model.Session) ([]byte, error) {
  16.         return json.Marshal(session)
  17. }
  18. func (r *RedisAdapter) UnSerialize(bytes []byte) (*model.Session, error) {
  19.         s := &model.Session{}
  20.         err := json.Unmarshal(bytes, s)
  21.         if err != nil {
  22.                 return nil, err
  23.         }
  24.         return s, nil
  25. }
  26. func (r *RedisAdapter) GetStr(key string) string {
  27.         res, err := r.client.Get(context.Background(), key).Result()
  28.         if err != nil {
  29.                 return ""
  30.         }
  31.         return res
  32. }
  33. func (r *RedisAdapter) SetStr(key string, value string, timeout int64) error {
  34.         err := r.client.Set(context.Background(), key, value, time.Duration(timeout)*time.Second).Err()
  35.         if err != nil {
  36.                 return err
  37.         }
  38.         return nil
  39. }
  40. func (r *RedisAdapter) UpdateStr(key string, value string) error {
  41.         err := r.client.Set(context.Background(), key, value, 0).Err()
  42.         if err != nil {
  43.                 return err
  44.         }
  45.         return nil
  46. }
  47. func (r *RedisAdapter) DeleteStr(key string) error {
  48.         err := r.client.Del(context.Background(), key).Err()
  49.         if err != nil {
  50.                 return err
  51.         }
  52.         return nil
  53. }
  54. func (r *RedisAdapter) GetStrTimeout(key string) int64 {
  55.         duration, err := r.client.TTL(context.Background(), key).Result()
  56.         if err != nil {
  57.                 return -1
  58.         }
  59.         return int64(duration.Seconds())
  60. }
  61. func (r *RedisAdapter) UpdateStrTimeout(key string, timeout int64) error {
  62.         var duration time.Duration
  63.         if timeout < 0 {
  64.                 duration = -1
  65.         } else {
  66.                 duration = time.Duration(timeout) * time.Second
  67.         }
  68.         err := r.client.Expire(context.Background(), key, duration).Err()
  69.         if err != nil {
  70.                 return err
  71.         }
  72.         return nil
  73. }
  74. func (r *RedisAdapter) Get(key string) interface{} {
  75.         res, err := r.client.Get(context.Background(), key).Result()
  76.         if err != nil {
  77.                 return nil
  78.         }
  79.         s := &model.Session{}
  80.         err = json.Unmarshal([]byte(res), s)
  81.         if err != nil {
  82.                 return nil
  83.         }
  84.         return s
  85. }
  86. func (r *RedisAdapter) Set(key string, value interface{}, timeout int64) error {
  87.         err := r.client.Set(context.Background(), key, value, time.Duration(timeout)*time.Second).Err()
  88.         if err != nil {
  89.                 return err
  90.         }
  91.         return nil
  92. }
  93. func (r *RedisAdapter) Update(key string, value interface{}) error {
  94.         err := r.client.Set(context.Background(), key, value, 0).Err()
  95.         if err != nil {
  96.                 return err
  97.         }
  98.         return nil
  99. }
  100. func (r *RedisAdapter) Delete(key string) error {
  101.         err := r.client.Del(context.Background(), key).Err()
  102.         if err != nil {
  103.                 return err
  104.         }
  105.         return nil
  106. }
  107. func (r *RedisAdapter) GetTimeout(key string) int64 {
  108.         duration, err := r.client.TTL(context.Background(), key).Result()
  109.         if err != nil {
  110.                 return -1
  111.         }
  112.         return int64(duration.Seconds())
  113. }
  114. func (r *RedisAdapter) UpdateTimeout(key string, timeout int64) error {
  115.         var duration time.Duration
  116.         if timeout < 0 {
  117.                 duration = -1
  118.         } else {
  119.                 duration = time.Duration(timeout) * time.Second
  120.         }
  121.         err := r.client.Expire(context.Background(), key, duration).Err()
  122.         if err != nil {
  123.                 return err
  124.         }
  125.         return nil
  126. }
  127. func (r *RedisAdapter) DeleteBatchFilteredKey(filterKeyPrefix string) error {
  128.         var cursor uint64 = 0
  129.         for {
  130.                 keys, cursor, err := r.client.Scan(context.Background(), cursor, filterKeyPrefix+"*", 100).Result()
  131.                 if err != nil {
  132.                         return err
  133.                 }
  134.                 if len(keys) == 0 && cursor == 0 {
  135.                         break
  136.                 }
  137.                 // use pip delete batch
  138.                 pipe := r.client.Pipeline()
  139.                 for _, key := range keys {
  140.                         pipe.Del(context.Background(), key)
  141.                 }
  142.                 _, err = pipe.Exec(context.Background())
  143.                 if err != nil {
  144.                         return err
  145.                 }
  146.         }
  147.         return nil
  148. }
复制代码
实现完接口,再写几个初始化方法
  1. func NewAdapter(addr string, username string, password string, db int) (*RedisAdapter, error) {
  2.         return NewAdapterByOptions(&redis.Options{
  3.                 Addr:     addr,
  4.                 Username: username,
  5.                 Password: password,
  6.                 DB:       db,
  7.         })
  8. }
  9. func NewAdapterByOptions(options *redis.Options) (*RedisAdapter, error) {
  10.         client := redis.NewClient(options)
  11.         _, err := client.Ping(context.Background()).Result()
  12.         if err != nil {
  13.                 return nil, err
  14.         }
  15.         return &RedisAdapter{client: client}, nil
  16. }
复制代码
测试

就不贴测试代码了,就放个链接~
token-go-extensions/adapter_test.go at master · weloe/token-go-extensions · GitHub
最后

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

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

tsx81429

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表