首页 > 其他分享 >用go设计开发一个自己的轻量级登录库/框架吧(拓展篇)

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

时间:2023-05-21 16:35:25浏览次数:46  
标签:return err nil RedisAdapter 登录 key go 轻量级 string

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

主库: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

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
}

测试

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

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

最后

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

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

标签:return,err,nil,RedisAdapter,登录,key,go,轻量级,string
From: https://www.cnblogs.com/weloe/p/17418722.html

相关文章

  • 友盟第三方登录和分享的使用
    获取友盟Appkey如果你之前已经在友盟注册了应用,并获取到了Appkey,可以继续使用它.如果你尚未在友盟注册开发者账号,需要先注册,注册之后登录你的账号,点击添加新应用,填写完应用基本信息后,将进入"下载SDK并添加代码"页面,此页面即可得到Appkey。下载并安装SDK下载[SDK最新版](http://dev.......
  • Google 又有新动作了
    阅读本文大概需要3分钟。不少人可能已经知道了,就在前两天,Google发布了一款loT(物联网)操作系统,取名AndroidThings,并且放出了开发者预览版本,供广大开发者们抢先体验,地址在:https://developer.android.com/things/index.html来张主页截图:看到这个主页大概也就知道这款操作系统的......
  • Asp.net MVC 3实例学习之ExtShop(六)——登录对话框
         登录对话框将使用jquery提供的对话框,所以不需要添加其它js文件。首先要为登录对话框添加一个表单模型。在Models目录下创建一个“AccountModels”类文件,然后添加一个Logon类,代码如下:1     public class LogOnModel2     {3      ......
  • 一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之登录窗口调试
         现在可以对登录窗口进行调试了。     打开系统的首页页面文件,Views\Home目录下的Index.cshtml文件。要动态加载登录窗口,需要先设置好加载路径,因而在onReady函数上加入以下代码:Ext.Loader.setConfig({true,paths:{'Ext.ux':'scripts/extjs/ux'......
  • PHP模拟登录并获取数据
    cURL是一个功能强大的PHP库,使用PHP的cURL库可以简单和有效地抓取网页并采集内容,设置cookie完成模拟登录网页,curl提供了丰富的函数,开发者可以从PHP手册中获取更多关于cURL信息。本文以模拟登录开源中国(oschina)为例,和大家分享cURL的使用。PHP的curl()在抓取网页的效率方面是比较......
  • mongodb慢查询记录
    在MySQL中,慢查询日志是经常作为我们优化数据库的依据,那在MongoDB中是否有类似的功能呢?答案是肯定的,那就是MongoDatabaseProfiler.不仅有,而且还有一些比MySQL的Slow QueryLog更详细的信息。它就是我们这篇文章的主题。开启Profiling功能有两种方式可以控制Profilin......
  • C++ 测试框架 GoogleTest 初学者入门篇 甲
    *以下内容为本人的学习笔记,如需要转载,请声明原文链接微信公众号「ENG八戒」https://mp.weixin.qq.com/s/BS_u9A4EY50y4vDDuxkCAQ开发者虽然主要负责工程里的开发任务,但是每个开发完毕的功能都是需要开发者自测通过的,所以经常会听到开发者提起单元测试的话题。那么今天我就带大伙......
  • Google Earth Engine(GEE)——美国建筑物数据集
    美国建筑物数据集国土安全部、FIMA、联邦紧急事务管理局的响应地理空间办公室、橡树岭国家实验室和美国地质调查局合作,建立和维护全国第一个大于450平方英尺的所有结构的清单,用于洪水保险缓解、应急准备和响应。为了创建建筑轮廓清单,联邦紧急事务管理局与国土安全部的科学和技术部......
  • Google Earth Engine(GEE)——全球建筑物数据集(MSBuildings数据集)包含微软7.77忆建筑物
    全球ML建筑脚印必应地图正在发布全球范围内的公开建筑脚印。我们从2014年至2021年的Bing地图图像中检测到777M的建筑,包括Maxar和Airbus的图像。为了完整起见,早期发布的数据集也包括在这个数据集中,并被纳入其中。你可以在这里找到Githubrepo和关于方法的更多信息。数据集是压缩的,......
  • Google Earth Engine(GEE) ——土著土地地图数据集
    土著土地(土著土地地图)土地确认是人们在日常生活中插入对土著存在和土地权利的认识的一种方式。这通常是在仪式、讲座或在本案例中的教育指南的开头进行。它可以是一种明确但有限的方式,承认殖民主义和第一民族的历史,以及定居者-殖民社会的变革需要。在这种情况下,我们希望在地理学上......