用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
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了,为什么我这里不往下写了?阳了好累当然是因为还在开发中~~
用go设计开发一个自己的轻量级登录库/框架吧(拓展篇)的更多相关文章
- 用Vue开发一个实时性时间转换功能,看这篇文章就够了
前言 最近有一个说法,如果你看见某个网站的某个功能,你就大概能猜出背后的业务逻辑是怎么样的,以及你能动手开发一个一毛一样的功能,那么你的前端技能算是进阶中高级水平了.比如咱们今天要聊的这个话题:如何用 ...
- C# 如何设计一个好用的日志库?【架构篇】
〇.前言 相信你在实际工作期间经常遇到或听到这样的说法: "我现在加一下日志,等会儿你再操作下." "只有在程序出问题以后才会知道打一个好的日志有多么重要.&qu ...
- HTML5实战教程———开发一个简单漂亮的登录页面
最近看过几个基于HTML5开发的移动应用,比如臭名昭著的12036移动客户端就是主要使用HTML5来实现的,虽然还是有点反应迟钝,但已经比较流畅了,相信随着智能手机的配置越来越高性能越来越好,会越来越 ...
- 准备开发一个基于canvas的图表库,记录一些东西(一)
开源的图表库已经有很多了,这里从头写个自己的,主要还是 提高自己js的水平,增加复杂代码组织的经验 首先写一个画图的库,供以后画图表使用.经过2天的开发,算是能拿出点东西了,虽然功能还很弱,但是有了一 ...
- 【Mac系统 + Python + Django】之开发一个发布会系统【Django视图(二)】
此学习资料是通过虫师的python接口自动化出的书学习而来的,在此说明一下,想学习更多的自动化的同学可以找虫师的博客园,非广告,因为我python+selenium自动化也是跟虫师学的,学习效果很好的 ...
- Smart Framework:轻量级 Java Web 框架
Smart Framework:轻量级 Java Web 框架 收藏 黄勇 工作闲暇之余,我开发了一款轻量级 Java Web 框架 —— Smart Framework. 开发该框架是为了: 加 ...
- Qt移动应用开发(二):使用动画框架
Qt移动应用开发(二):使用动画框架 上一篇博客介绍了怎样使用Qt的QML来对屏幕分辨率大小进行适应,其实,不同分辨率的适应是一个很棘手的问题,除了分辨率不同外,宽高比(aspect ratio)也不 ...
- 给力的轻量级JavaScript动画框架 - jsMorph
jsMorph 是一个独立的轻量级 JavaScript 动画框架,可以用它来操纵多个 HTML 元素的样式,实现动画效果.此框架会自动检测起始位置.转换单位.调整渲染的速度,以此来获得更流畅的渲染体 ...
- 巧用第三方高速开发Android App 热门第三方SDK及框架
巧用第三方高速开发Android App 热门第三方SDK及框架 历经大半年的时间,最终是把这门课程给录制出来了,也就在今天,正式在慕课网上上线了 项目地址:巧用第三方高速开发Android App ...
- 巧用第三方快速开发Android App 热门第三方SDK及框架
巧用第三方快速开发Android App 热门第三方SDK及框架 历经大半年的时间,终于是把这门课程给录制出来了,也就在今天,正式在慕课网上上线了 项目地址:巧用第三方快速开发Android App ...
随机推荐
- 被冰封的 Bug:Fishhook Crash 修复纪实
作者:郝连福,业界资深计算机技术专家,现任声网Agora 首席前端架构师.先后担任过 Principal Engineer/Engineering Director(UTStarcom).Sr. ar ...
- Vditor在原生JS中如何结合后端使用
目录 1.Vditor介绍 2.如何在原生JS中结合后端使用 2.1 背景 2.2 正确使用方式 2.2.1 编辑页面 2.2.2 回显页面(修改页面) 2.2.3 预览页面 3.小结一下 1.Vdi ...
- Treemap按key和value降序排序
Treemap是一种根据键排序的数据结构,可以通过重载它的比较器来按照值排序.要按键排序,可以使用默认的比较器,而要按值排序,可以创建一个自定义的比较器并将其传递给treemap的构造函数. 以下是按 ...
- Helm 安装 Kubernetes 监控套件
Helm 安装 Grafana Prometheus Altermanager 套件 安装helm # 安装helm工具 curl -fsSL -o get_helm.sh https://raw.g ...
- 0001 嵌入式开发带你从小白到大佬系列之——Linux开发环境搭建—Windows-VMware-Ubuntu环境配置
如文章标题,我们安装的Linux开发环境是:Windows-VMware-Ubuntu环境 配置,即在windows系统下安装VMware虚拟机,之后在VMware中配置安装Linux系统的常用发行版 ...
- Springboot一些常用注解
Springboot启动注解 @SpringbootApplication 这个注解是Springboot最核心的注解,用在Springboot的主类上,标识这是一个Springboot应用,用来开启 ...
- day32:进程&进程join&守护进程deamon
目录 1.进程的基本概念 2.进程初体验 3.join:先子后主 4.守护进程:deamon 5.使用自定义类的方式创建进程 6.两张和进程相关的图 进程的基本概念 什么是进程? 进程就是正在运行的程 ...
- 【机器学习入门与实践】数据挖掘-二手车价格交易预测(含EDA探索、特征工程、特征优化、模型融合等)
[机器学习入门与实践]数据挖掘-二手车价格交易预测(含EDA探索.特征工程.特征优化.模型融合等) note:项目链接以及码源见文末 1.赛题简介 了解赛题 赛题概况 数据概况 预测指标 分析赛题 数 ...
- 微信Token验证代码的实现
微信开放第三方API接口, 申请地址: https://mp.weixin.qq.com/advanced/advanced?action=interface&t=advanced/inter ...
- 研发运维双管齐下!Seal AppManager的正确打开方式
新一代应用统一部署管理平台 Seal AppManager 采用平台工程的理念,通过降低基础设施操作的复杂度为研发和运维团队提供易用.一致的应用管理和部署体验.Seal AppManager 帮助研发 ...