url_mapping.query.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. package query
  2. import (
  3. "fmt"
  4. "go-admin/app/observe/models"
  5. "math/rand"
  6. "strconv"
  7. "sync"
  8. "time"
  9. log "github.com/go-admin-team/go-admin-core/logger"
  10. "github.com/go-redis/redis/v7"
  11. "github.com/pkg/errors"
  12. )
  13. type UrlMapping struct {
  14. Query
  15. }
  16. func NewUrlMapping() UrlMapping {
  17. q := Query{}
  18. q.Init()
  19. return UrlMapping{q}
  20. }
  21. // 通过一个url匹配表中的kind,即匹配表中的url,表中的url为正则
  22. func (q UrlMapping) UrlToKind(appId int64, url string) (kind string, err error) {
  23. err = q.db.Model(&models.UrlMapping{}).Where("app_id", appId).Where("? REGEXP CONCAT('^', url, '$')", url).Pluck("url", &kind).Error
  24. return
  25. }
  26. // 这里的url参数,必须不包括?后的参数,仅path部分,否则会有很多
  27. func (q UrlMapping) UrlToKindCache(appId int64, url string) (kind string, err error) {
  28. key := fmt.Sprintf("observe__url_mapping_url_kind_%d_%s", appId, url)
  29. if kind, err = q.rdb.Get(key).Result(); err == nil {
  30. return
  31. } else if err == redis.Nil { // 说明不存在该key
  32. kind, err = q.UrlToKind(appId, url)
  33. if err != nil {
  34. return
  35. }
  36. q.rdb.Set(key, kind, time.Hour) // 缓存一小时,这里即使kind为空也缓存,防止一直访问数据库
  37. }
  38. return
  39. }
  40. func (q UrlMapping) UrlToType(appId int64, url string) (t int, err error) {
  41. err = q.db.Model(&models.UrlMapping{}).Where("app_id", appId).Where("? REGEXP CONCAT('^', url, '$')", url).Pluck("type", &t).Error
  42. return
  43. }
  44. func (q UrlMapping) UrlToTypeCache(appId int64, url string) (t int, err error) {
  45. key := fmt.Sprintf("observe__url_mapping_url_type_%d_%s", appId, url)
  46. var ts string
  47. if ts, err = q.rdb.Get(key).Result(); err == nil {
  48. t, err = strconv.Atoi(ts)
  49. return
  50. } else if err == redis.Nil { // 说明不存在该key
  51. t, err = q.UrlToType(appId, url)
  52. if err != nil {
  53. return
  54. }
  55. q.rdb.Set(key, t, time.Hour) // 缓存一小时,这里即使t为0也缓存,防止一直访问数据库
  56. }
  57. return
  58. }
  59. type urlMap struct {
  60. ExpireAt time.Time
  61. SafeMap *sync.Map
  62. // Map map[string]int
  63. }
  64. // var app2UrlMap map[int64]urlMap = map[int64]urlMap{}
  65. var app2UrlMap sync.Map = sync.Map{}
  66. func (q UrlMapping) UrlToTypeFast(appId int64, url string) (t int, err error) {
  67. if umi, ok := app2UrlMap.Load(appId); ok {
  68. um := umi.(*urlMap)
  69. if um.ExpireAt.After(time.Now()) {
  70. if t, ok := um.SafeMap.Load(url); ok {
  71. return t.(int), nil
  72. } else {
  73. return 0, errors.New(fmt.Sprintf("缓存中未获取到url type, appid: %d, url: %s", appId, url))
  74. }
  75. }
  76. }
  77. // 分布式锁
  78. lock := fmt.Sprintf("urlmap_lock_%d", appId)
  79. b, err := q.rdb.SetNX(lock, 1, time.Second*30).Result()
  80. if err != nil {
  81. return 0, errors.Wrap(err, fmt.Sprintf("分布式锁设置失败: %d", appId))
  82. }
  83. if !b {
  84. return 0, errors.New(fmt.Sprintf("分布式锁生效中: %d", appId))
  85. }
  86. defer q.rdb.Del(lock)
  87. urlList := []struct {
  88. Url string
  89. Type int
  90. }{}
  91. err = q.db.Model(&models.UrlMapping{}).Select("url, type").Where("app_id", appId).Where("is_perfect_match", 1).Find(&urlList).Error
  92. if err != nil {
  93. return 0, errors.Wrap(err, fmt.Sprintf("获取应用%d下urlList失败", appId))
  94. }
  95. seconds := time.Second * time.Duration(rand.Intn(600)+300) // 缓存 10~15 分钟, 目的就防止同时失效导致多个应用并发读库
  96. um := &urlMap{
  97. ExpireAt: time.Now().Add(seconds),
  98. SafeMap: &sync.Map{},
  99. }
  100. err = errors.New(fmt.Sprintf("url: %s 不存在", url))
  101. for _, urlItem := range urlList {
  102. um.SafeMap.Store(urlItem.Url, urlItem.Type)
  103. if urlItem.Url == url {
  104. t, err = urlItem.Type, nil
  105. }
  106. }
  107. app2UrlMap.Store(appId, um)
  108. return t, err
  109. }
  110. func (q UrlMapping) Exists(appAlias, serviceName, method, route string) (bool, error) {
  111. var count int64
  112. if err := q.db.Model(&models.UrlMapping{}).
  113. Where("app_alias=? and service_name=? and method=? and url=?", appAlias, serviceName, method, route).
  114. Count(&count).Error; err != nil {
  115. return false, errors.Wrap(err, "查询失败")
  116. }
  117. return count > 0, nil
  118. }
  119. func (q UrlMapping) ExistsCache(appAlias, serviceName, method, route string) (bool, error) {
  120. key := fmt.Sprintf("observe__url_mapping_exists_%s_%s_%s_%s", appAlias, serviceName, method, route)
  121. if val, err := q.rdb.Get(key).Result(); err == nil {
  122. if val == "1" {
  123. return true, nil
  124. }
  125. return false, nil
  126. }
  127. exists, err := q.Exists(appAlias, serviceName, method, route)
  128. if err != nil {
  129. return false, err
  130. }
  131. val := 0
  132. if exists {
  133. val = 1
  134. }
  135. q.rdb.Set(key, val, 10*time.Minute)
  136. return exists, nil
  137. }
  138. func (q UrlMapping) UrlMappingID(appAlias, serviceName, method, route string) int {
  139. key := fmt.Sprintf("observe__biz_url_mapping_id_app_%s", appAlias) // 为了防止value过大,每个appid存一个
  140. field := fmt.Sprintf("%s-%s-%s", serviceName, method, route)
  141. id, err := q.rdb.HGet(key, field).Result()
  142. if err == nil { // 如果获取不到数据, err 为 redis.Nil, err == nil时,肯定获取到了数据
  143. res, _ := strconv.Atoi(id)
  144. return res
  145. } else if err == redis.Nil {
  146. log.Debugf("key: %s, field: %s不存在", key, field)
  147. }
  148. urlmappings := []struct {
  149. ServiceName string
  150. Method string
  151. Url string
  152. Id int
  153. }{}
  154. if err := q.db.Model(&models.UrlMapping{}).Select("service_name, method, url, id").
  155. Where("app_alias=?", appAlias).Find(&urlmappings).Error; err != nil {
  156. log.Errorf("获取url mapping信息失败: app_alias %s", appAlias)
  157. return 0
  158. }
  159. vals := map[string]interface{}{}
  160. for _, item := range urlmappings {
  161. field := fmt.Sprintf("%s-%s-%s", item.ServiceName, item.Method, item.Url)
  162. vals[field] = item.Id
  163. }
  164. q.rdb.HSet(key, vals)
  165. q.rdb.Expire(key, time.Minute*5)
  166. if val, ok := vals[field]; ok {
  167. return val.(int)
  168. }
  169. log.Debugf("url mapping表中不存在该数据(app_alias:%s, serviceName:%s, method:%s, url:%s)", appAlias, serviceName, method, route)
  170. return 0
  171. }
  172. func (q UrlMapping) UrlToName(appAlias, method, url string) (string, error) {
  173. var name string
  174. db := q.db.Model(&models.UrlMapping{}).
  175. Where("app_alias", appAlias).Where("url", url).Where("is_perfect_match", 1)
  176. if method != "" {
  177. db.Where("method", method)
  178. }
  179. db.Pluck("name", &name)
  180. if err := db.Pluck("name", &name).Error; err != nil {
  181. return "", err
  182. }
  183. return name, nil
  184. }
  185. func (q UrlMapping) UrlToNameCache(appAlias, method, url string) (string, error) {
  186. key := fmt.Sprintf("observe__biz_url_to_name_%s_%s_%s", appAlias, method, url)
  187. name, err := q.rdb.Get(key).Result()
  188. if err == nil {
  189. return name, nil
  190. }
  191. name, err = q.UrlToName(appAlias, method, url)
  192. if err != nil {
  193. return "", err
  194. }
  195. q.rdb.Set(key, name, time.Minute*10)
  196. return name, nil
  197. }