服务端开发、测试过程中或多或少都会遇到白名单,来满足具备时效性的活动、用户命中条件特殊,以及线上测试账号进行构造等各种需求。
当越来越多的服务有白名单的需要时,如何高效的管理白名单呢?
梳理业务中存在白名单的地方,无非不同功能存在一批白名单账号配置,因此可以将配置抽象成账号和标签,有的功能需要模拟到期、下线等情况,标签还需要支持有效期。不同标签用作不同场景。
可以新建一个单独的服务来管理维护众多的白名单账号及标签,或使用内部的配置平台来维护相关配置。
各业务调用时,可以以sdk的方式提供给业务方,下面将以golang为例
SDK代码示例
package sdk
import (
"sync"
"time"
)
type TagData struct {
// 白名单标签,简短的英文字符表示
Tag string `json:"tag"`
// 描述信息,说明使用用途
Desc string `json:"desc"`
// 有效期开始时间,<=0 不进行校验
Stime string `json:"stime"`
// 有效期结束时间,<=0 不进行校验
Etiem string `json:"etime"`
}
type AccountData struct {
// 测试账号唯一标识,可以为用户ID或用户名
User string `json:"user"`
// 账号具有的白名单标签
Tags map[string]bool `json:"tags"`
}
type WhiteAccountConf struct {
Accounts map[string]*AccountData
Tags map[string]*TagData
// 读写锁
lock sync.RWMutex
// 初次加载标记,用于惰性加载
first bool
// 定时加载时间
loop time.Duration
// 退出时发送消息到此通道
stop chan bool
// 是否启动标记
isStarted bool
}
var (
WAConfig *WhiteAccountConf = &WhiteAccountConf{}
)
// 加载全量白名单配置到内存
func (w *WhiteAccountConf) loadConfig() {
// 标记初次加载
if !w.first {
w.first = true
}
// 查询全量白名单配置
allConf, err := ...
if err != nil {
return
}
// 写锁
w.lock.Lock()
defer w.lock.Unlock()
// 根据查询到的全量配置更新内存
w.Accounts = allConf.Accounts // 需根据实际进行调整
w.Tags = allConf.Tags // 需根据实际进行调整
return
}
// 初始化
func (w *WhiteAccountConf) Init(loop time.Duration) {
w.loop = loop
w.stop = make(chan bool)
}
// 定时加载配置
func (w *WhiteAccountConf) start() {
if w.isStarted {
return
}
tk := time.NewTicker(w.loop)
defer tk.Stop()
for {
select {
case <- tk.C:
w.loadConfig()
case <- w.stop:
return
}
}
}
// 异步执行定时加载配置
func (w *WhiteAccountConf) Run() {
w.isStarted = true
go w.start()
}
// 停止加载白名单配置
func (w *WhiteAccountConf) Stop() {
if !w.isStarted {
return
}
w.stop <- true
}
// 检查是否是白名单账号
func (w *WhiteAccountConf) Check(user, tag string) bool {
if !w.first {
w.loadConfig()
}
tagData, ok := w.Tags[tag]
if !ok {
return false
}
now := time.Now().Unix()
if (tag.Data.Stime > 0 && tag.Data.Stime > now) || (tag.Data.Etime > 0 && tag.Data.Etime < now) {
return false
}
userData, ok := w.Accounts[user]
if !ok {
return false
}
return userData.Tags[tag]
}
业务方调用
main.go文件中需要先初始化加载
package main
import (
"time"
"xxx/white-account/sdk"
)
func xxxUse() {
if sdk.WAConfig.Check("zhangsan", "tag1") {
fmt.Println("zhangsan is a white account")
} else {
fmt.Println("zhangsan is not a white account")
}
}
func main() {
// 初始化,每5分钟加载一次最新白名单配置,可以设置更长时间
sdk.WAConfig.Init(300 * time.Second)
// 开始定时拉取
sdk.WAConfig.Run()
// 服务停止时结束定时拉取
defer sdk.WAConfig.Stop()
}