123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- package query
- import (
- "fmt"
- "go-admin/app/observe/models"
- "math/rand"
- "strconv"
- "sync"
- "time"
- log "github.com/go-admin-team/go-admin-core/logger"
- "github.com/go-redis/redis/v7"
- "github.com/pkg/errors"
- )
- type UrlMapping struct {
- Query
- }
- func NewUrlMapping() UrlMapping {
- q := Query{}
- q.Init()
- return UrlMapping{q}
- }
- // 通过一个url匹配表中的kind,即匹配表中的url,表中的url为正则
- func (q UrlMapping) UrlToKind(appId int64, url string) (kind string, err error) {
- err = q.db.Model(&models.UrlMapping{}).Where("app_id", appId).Where("? REGEXP CONCAT('^', url, '$')", url).Pluck("url", &kind).Error
- return
- }
- // 这里的url参数,必须不包括?后的参数,仅path部分,否则会有很多
- func (q UrlMapping) UrlToKindCache(appId int64, url string) (kind string, err error) {
- key := fmt.Sprintf("observe__url_mapping_url_kind_%d_%s", appId, url)
- if kind, err = q.rdb.Get(key).Result(); err == nil {
- return
- } else if err == redis.Nil { // 说明不存在该key
- kind, err = q.UrlToKind(appId, url)
- if err != nil {
- return
- }
- q.rdb.Set(key, kind, time.Hour) // 缓存一小时,这里即使kind为空也缓存,防止一直访问数据库
- }
- return
- }
- func (q UrlMapping) UrlToType(appId int64, url string) (t int, err error) {
- err = q.db.Model(&models.UrlMapping{}).Where("app_id", appId).Where("? REGEXP CONCAT('^', url, '$')", url).Pluck("type", &t).Error
- return
- }
- func (q UrlMapping) UrlToTypeCache(appId int64, url string) (t int, err error) {
- key := fmt.Sprintf("observe__url_mapping_url_type_%d_%s", appId, url)
- var ts string
- if ts, err = q.rdb.Get(key).Result(); err == nil {
- t, err = strconv.Atoi(ts)
- return
- } else if err == redis.Nil { // 说明不存在该key
- t, err = q.UrlToType(appId, url)
- if err != nil {
- return
- }
- q.rdb.Set(key, t, time.Hour) // 缓存一小时,这里即使t为0也缓存,防止一直访问数据库
- }
- return
- }
- type urlMap struct {
- ExpireAt time.Time
- SafeMap *sync.Map
- // Map map[string]int
- }
- // var app2UrlMap map[int64]urlMap = map[int64]urlMap{}
- var app2UrlMap sync.Map = sync.Map{}
- func (q UrlMapping) UrlToTypeFast(appId int64, url string) (t int, err error) {
- if umi, ok := app2UrlMap.Load(appId); ok {
- um := umi.(*urlMap)
- if um.ExpireAt.After(time.Now()) {
- if t, ok := um.SafeMap.Load(url); ok {
- return t.(int), nil
- } else {
- return 0, errors.New(fmt.Sprintf("缓存中未获取到url type, appid: %d, url: %s", appId, url))
- }
- }
- }
- // 分布式锁
- lock := fmt.Sprintf("urlmap_lock_%d", appId)
- b, err := q.rdb.SetNX(lock, 1, time.Second*30).Result()
- if err != nil {
- return 0, errors.Wrap(err, fmt.Sprintf("分布式锁设置失败: %d", appId))
- }
- if !b {
- return 0, errors.New(fmt.Sprintf("分布式锁生效中: %d", appId))
- }
- defer q.rdb.Del(lock)
- urlList := []struct {
- Url string
- Type int
- }{}
- err = q.db.Model(&models.UrlMapping{}).Select("url, type").Where("app_id", appId).Where("is_perfect_match", 1).Find(&urlList).Error
- if err != nil {
- return 0, errors.Wrap(err, fmt.Sprintf("获取应用%d下urlList失败", appId))
- }
- seconds := time.Second * time.Duration(rand.Intn(600)+300) // 缓存 10~15 分钟, 目的就防止同时失效导致多个应用并发读库
- um := &urlMap{
- ExpireAt: time.Now().Add(seconds),
- SafeMap: &sync.Map{},
- }
- err = errors.New(fmt.Sprintf("url: %s 不存在", url))
- for _, urlItem := range urlList {
- um.SafeMap.Store(urlItem.Url, urlItem.Type)
- if urlItem.Url == url {
- t, err = urlItem.Type, nil
- }
- }
- app2UrlMap.Store(appId, um)
- return t, err
- }
- func (q UrlMapping) Exists(appAlias, serviceName, method, route string) (bool, error) {
- var count int64
- if err := q.db.Model(&models.UrlMapping{}).
- Where("app_alias=? and service_name=? and method=? and url=?", appAlias, serviceName, method, route).
- Count(&count).Error; err != nil {
- return false, errors.Wrap(err, "查询失败")
- }
- return count > 0, nil
- }
- func (q UrlMapping) ExistsCache(appAlias, serviceName, method, route string) (bool, error) {
- key := fmt.Sprintf("observe__url_mapping_exists_%s_%s_%s_%s", appAlias, serviceName, method, route)
- if val, err := q.rdb.Get(key).Result(); err == nil {
- if val == "1" {
- return true, nil
- }
- return false, nil
- }
- exists, err := q.Exists(appAlias, serviceName, method, route)
- if err != nil {
- return false, err
- }
- val := 0
- if exists {
- val = 1
- }
- q.rdb.Set(key, val, 10*time.Minute)
- return exists, nil
- }
- func (q UrlMapping) UrlMappingID(appAlias, serviceName, method, route string) int {
- key := fmt.Sprintf("observe__biz_url_mapping_id_app_%s", appAlias) // 为了防止value过大,每个appid存一个
- field := fmt.Sprintf("%s-%s-%s", serviceName, method, route)
- id, err := q.rdb.HGet(key, field).Result()
- if err == nil { // 如果获取不到数据, err 为 redis.Nil, err == nil时,肯定获取到了数据
- res, _ := strconv.Atoi(id)
- return res
- } else if err == redis.Nil {
- log.Debugf("key: %s, field: %s不存在", key, field)
- }
- urlmappings := []struct {
- ServiceName string
- Method string
- Url string
- Id int
- }{}
- if err := q.db.Model(&models.UrlMapping{}).Select("service_name, method, url, id").
- Where("app_alias=?", appAlias).Find(&urlmappings).Error; err != nil {
- log.Errorf("获取url mapping信息失败: app_alias %s", appAlias)
- return 0
- }
- vals := map[string]interface{}{}
- for _, item := range urlmappings {
- field := fmt.Sprintf("%s-%s-%s", item.ServiceName, item.Method, item.Url)
- vals[field] = item.Id
- }
- q.rdb.HSet(key, vals)
- q.rdb.Expire(key, time.Minute*5)
- if val, ok := vals[field]; ok {
- return val.(int)
- }
- log.Debugf("url mapping表中不存在该数据(app_alias:%s, serviceName:%s, method:%s, url:%s)", appAlias, serviceName, method, route)
- return 0
- }
- func (q UrlMapping) UrlToName(appAlias, method, url string) (string, error) {
- var name string
- db := q.db.Model(&models.UrlMapping{}).
- Where("app_alias", appAlias).Where("url", url).Where("is_perfect_match", 1)
- if method != "" {
- db.Where("method", method)
- }
- db.Pluck("name", &name)
- if err := db.Pluck("name", &name).Error; err != nil {
- return "", err
- }
- return name, nil
- }
- func (q UrlMapping) UrlToNameCache(appAlias, method, url string) (string, error) {
- key := fmt.Sprintf("observe__biz_url_to_name_%s_%s_%s", appAlias, method, url)
- name, err := q.rdb.Get(key).Result()
- if err == nil {
- return name, nil
- }
- name, err = q.UrlToName(appAlias, method, url)
- if err != nil {
- return "", err
- }
- q.rdb.Set(key, name, time.Minute*10)
- return name, nil
- }
|