redis.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. package dao
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "strconv"
  7. "go-common/app/interface/openplatform/article/model"
  8. "go-common/library/cache/redis"
  9. "go-common/library/log"
  10. )
  11. const (
  12. _prefixUpper = "art_u_%d" // upper's article list
  13. _prefixSorted = "art_sort_%d_%d" // sorted aids sort_category_field
  14. _prefixRank = "art_ranks_%d" // ranks by cid
  15. _prefixMaxLike = "art_mlt_%d" // like message number
  16. _readPingSet = "art:readping" // reading start set
  17. _prefixReadPing = "art:readping:%s:%d" // reading during on some device for some article
  18. _blank = int64(-1)
  19. )
  20. func upperKey(mid int64) string {
  21. return fmt.Sprintf(_prefixUpper, mid)
  22. }
  23. func sortedKey(categoryID int64, field int) string {
  24. return fmt.Sprintf(_prefixSorted, categoryID, field)
  25. }
  26. func rankKey(cid int64) string {
  27. return fmt.Sprintf(_prefixRank, cid)
  28. }
  29. func hotspotKey(typ int8, id int64) string {
  30. return fmt.Sprintf("art_hotspot%d_%d", typ, id)
  31. }
  32. func authorCategoriesKey(mid int64) string {
  33. return fmt.Sprintf("author:categories:%d", mid)
  34. }
  35. func recommendsAuthorsKey(category int64) string {
  36. return fmt.Sprintf("recommends:authors:%d", category)
  37. }
  38. func readPingSetKey() string {
  39. return _readPingSet
  40. }
  41. func readPingKey(buvid string, aid int64) string {
  42. return fmt.Sprintf(_prefixReadPing, buvid, aid)
  43. }
  44. // pingRedis ping redis.
  45. func (d *Dao) pingRedis(c context.Context) (err error) {
  46. conn := d.redis.Get(c)
  47. if _, err = conn.Do("SET", "PING", "PONG"); err != nil {
  48. PromError("redis: ping remote")
  49. log.Error("remote redis: conn.Do(SET,PING,PONG) error(%+v)", err)
  50. }
  51. conn.Close()
  52. return
  53. }
  54. // ExpireUpperCache expire the upper key.
  55. func (d *Dao) ExpireUpperCache(c context.Context, mid int64) (ok bool, err error) {
  56. conn := d.redis.Get(c)
  57. defer conn.Close()
  58. if ok, err = redis.Bool(conn.Do("EXPIRE", upperKey(mid), d.redisUpperExpire)); err != nil {
  59. PromError("redis:up主设定过期")
  60. log.Error("conn.Send(EXPIRE, %s) error(%+v)", upperKey(mid), err)
  61. }
  62. return
  63. }
  64. // ExpireUppersCache expire the upper key.
  65. func (d *Dao) ExpireUppersCache(c context.Context, mids []int64) (res map[int64]bool, err error) {
  66. conn := d.redis.Get(c)
  67. defer conn.Close()
  68. res = make(map[int64]bool, len(mids))
  69. for _, mid := range mids {
  70. if err = conn.Send("EXPIRE", upperKey(mid), d.redisUpperExpire); err != nil {
  71. PromError("redis:up主设定过期")
  72. log.Error("conn.Send(EXPIRE, %s) error(%+v)", upperKey(mid), err)
  73. return
  74. }
  75. }
  76. if err = conn.Flush(); err != nil {
  77. PromError("redis:up主flush")
  78. log.Error("conn.Flush error(%+v)", err)
  79. return
  80. }
  81. var ok bool
  82. for _, mid := range mids {
  83. if ok, err = redis.Bool(conn.Receive()); err != nil {
  84. PromError("redis:up主receive")
  85. log.Error("conn.Receive() error(%+v)", err)
  86. return
  87. }
  88. res[mid] = ok
  89. }
  90. return
  91. }
  92. // UppersCaches batch get new articles of uppers by cache.
  93. func (d *Dao) UppersCaches(c context.Context, mids []int64, start, end int) (res map[int64][]int64, err error) {
  94. conn := d.redis.Get(c)
  95. defer conn.Close()
  96. res = make(map[int64][]int64, len(mids))
  97. for _, mid := range mids {
  98. if err = conn.Send("ZREVRANGE", upperKey(mid), start, end); err != nil {
  99. PromError("redis:获取up主")
  100. log.Error("conn.Send(%s) error(%+v)", upperKey(mid), err)
  101. return
  102. }
  103. }
  104. if err = conn.Flush(); err != nil {
  105. PromError("redis:获取up主flush")
  106. log.Error("conn.Flush error(%+v)", err)
  107. return
  108. }
  109. for _, mid := range mids {
  110. aids, err := redis.Int64s(conn.Receive())
  111. if err != nil {
  112. PromError("redis:获取up主receive")
  113. log.Error("conn.Send(ZREVRANGE, %d) error(%+v)", mid, err)
  114. }
  115. l := len(aids)
  116. if l == 0 {
  117. continue
  118. }
  119. if aids[l-1] == _blank {
  120. aids = aids[:l-1]
  121. }
  122. res[mid] = aids
  123. }
  124. cachedCount.Add("up", int64(len(res)))
  125. return
  126. }
  127. // AddUpperCache adds passed article of upper.
  128. func (d *Dao) AddUpperCache(c context.Context, mid, aid int64, ptime int64) (err error) {
  129. art := map[int64][][2]int64{mid: [][2]int64{[2]int64{aid, ptime}}}
  130. err = d.AddUpperCaches(c, art)
  131. return
  132. }
  133. // AddUpperCaches batch add passed article of upper.
  134. func (d *Dao) AddUpperCaches(c context.Context, idsm map[int64][][2]int64) (err error) {
  135. var (
  136. mid, aid, ptime int64
  137. arts [][2]int64
  138. conn = d.redis.Get(c)
  139. count int
  140. )
  141. defer conn.Close()
  142. for mid, arts = range idsm {
  143. key := upperKey(mid)
  144. if len(arts) == 0 {
  145. arts = [][2]int64{[2]int64{_blank, _blank}}
  146. }
  147. for _, art := range arts {
  148. aid = art[0]
  149. ptime = art[1]
  150. if err = conn.Send("ZADD", key, "CH", ptime, aid); err != nil {
  151. PromError("redis:增加up主缓存")
  152. log.Error("conn.Send(ZADD, %s, %d, %d) error(%+v)", key, aid, err)
  153. return
  154. }
  155. count++
  156. }
  157. if err = conn.Send("EXPIRE", key, d.redisUpperExpire); err != nil {
  158. PromError("redis:增加up主expire")
  159. log.Error("conn.Expire error(%+v)", err)
  160. return
  161. }
  162. count++
  163. }
  164. if err = conn.Flush(); err != nil {
  165. PromError("redis:增加up主flush")
  166. log.Error("conn.Flush error(%+v)", err)
  167. return
  168. }
  169. for i := 0; i < count; i++ {
  170. if _, err = conn.Receive(); err != nil {
  171. PromError("redis:增加up主receive")
  172. log.Error("conn.Receive error(%+v)", err)
  173. return
  174. }
  175. }
  176. return
  177. }
  178. // DelUpperCache delete article of upper cache.
  179. func (d *Dao) DelUpperCache(c context.Context, mid int64, aid int64) (err error) {
  180. conn := d.redis.Get(c)
  181. defer conn.Close()
  182. if _, err = conn.Do("ZREM", upperKey(mid), aid); err != nil {
  183. PromError("redis:删除up主")
  184. log.Error("conn.Do(ZERM, %s, %d) error(%+v)", upperKey(mid), aid, err)
  185. }
  186. return
  187. }
  188. // UpperArtsCountCache get upper articles count
  189. func (d *Dao) UpperArtsCountCache(c context.Context, mid int64) (res int, err error) {
  190. conn := d.redis.Get(c)
  191. defer conn.Close()
  192. if res, err = redis.Int(conn.Do("ZCOUNT", upperKey(mid), 0, "+inf")); err != nil {
  193. PromError("redis:up主文章计数")
  194. log.Error("conn.Do(ZCARD, %s) error(%+v)", upperKey(mid), err)
  195. }
  196. return
  197. }
  198. // MoreArtsCaches batch get early articles of upper by publish time.
  199. func (d *Dao) MoreArtsCaches(c context.Context, mid, ptime int64, num int) (before []int64, after []int64, err error) {
  200. conn := d.redis.Get(c)
  201. defer conn.Close()
  202. if err = conn.Send("ZREVRANGEBYSCORE", upperKey(mid), fmt.Sprintf("(%d", ptime), "-inf", "LIMIT", 0, num); err != nil {
  203. PromError("redis:获取up主更早文章")
  204. log.Error("conn.Send(%s) error(%+v)", upperKey(mid), err)
  205. return
  206. }
  207. if err = conn.Send("ZRANGEBYSCORE", upperKey(mid), fmt.Sprintf("(%d", ptime), "+inf", "LIMIT", 0, num); err != nil {
  208. PromError("redis:获取up主更晚文章")
  209. log.Error("conn.Send(%s) error(%+v)", upperKey(mid), err)
  210. return
  211. }
  212. if err = conn.Flush(); err != nil {
  213. PromError("redis:获取up主更晚文章")
  214. log.Error("conn.Flush error(%+v)", err)
  215. return
  216. }
  217. if before, err = redis.Int64s(conn.Receive()); err != nil {
  218. PromError("redis:获取up主更早文章")
  219. log.Error("conn.Receive error(%+v)", err)
  220. return
  221. }
  222. if after, err = redis.Int64s(conn.Receive()); err != nil {
  223. PromError("redis:获取up主更晚文章")
  224. log.Error("conn.Receive error(%+v)", err)
  225. return
  226. }
  227. l := len(before)
  228. if l == 0 {
  229. return
  230. }
  231. if before[l-1] == _blank {
  232. before = before[:l-1]
  233. }
  234. return
  235. }
  236. // ExpireRankCache expire rank cache
  237. func (d *Dao) ExpireRankCache(c context.Context, cid int64) (res bool, err error) {
  238. conn := d.redis.Get(c)
  239. defer conn.Close()
  240. var ttl int64
  241. if ttl, err = redis.Int64(conn.Do("TTL", rankKey(cid))); err != nil {
  242. PromError("redis:排行榜expire")
  243. log.Error("ExpireRankCache(ttl %s) error(%+v)", rankKey(cid), err)
  244. return
  245. }
  246. if ttl > (d.redisRankTTL - d.redisRankExpire) {
  247. res = true
  248. return
  249. }
  250. return
  251. }
  252. // RankCache get rank cache
  253. func (d *Dao) RankCache(c context.Context, cid int64) (res model.RankResp, err error) {
  254. conn := d.redis.Get(c)
  255. defer conn.Close()
  256. key := rankKey(cid)
  257. var s string
  258. if s, err = redis.String(conn.Do("GET", key)); err != nil {
  259. if err == redis.ErrNil {
  260. err = nil
  261. return
  262. }
  263. PromError("redis:获取排行榜")
  264. log.Error("dao.RankCache zrevrange(%s) err: %+v", key, err)
  265. return
  266. }
  267. err = json.Unmarshal([]byte(s), &res)
  268. return
  269. }
  270. // AddRankCache add rank cache
  271. func (d *Dao) AddRankCache(c context.Context, cid int64, arts model.RankResp) (err error) {
  272. var (
  273. key = rankKey(cid)
  274. conn = d.redis.Get(c)
  275. count int
  276. )
  277. defer conn.Close()
  278. if len(arts.List) == 0 {
  279. return
  280. }
  281. if err = conn.Send("DEL", key); err != nil {
  282. PromError("redis:删除排行榜缓存")
  283. log.Error("conn.Send(DEL, %s) error(%+v)", key, err)
  284. return
  285. }
  286. count++
  287. value, _ := json.Marshal(arts)
  288. if err = conn.Send("SET", key, value); err != nil {
  289. PromError("redis:增加排行榜缓存")
  290. log.Error("conn.Send(SET, %s, %s) error(%+v)", key, value, err)
  291. return
  292. }
  293. count++
  294. if err = conn.Send("EXPIRE", key, d.redisRankTTL); err != nil {
  295. PromError("redis:expire排行榜")
  296. log.Error("conn.Send(EXPIRE, %s, %v) error(%+v)", key, d.redisRankTTL, err)
  297. return
  298. }
  299. count++
  300. if err = conn.Flush(); err != nil {
  301. PromError("redis:增加排行榜flush")
  302. log.Error("conn.Flush error(%+v)", err)
  303. return
  304. }
  305. for i := 0; i < count; i++ {
  306. if _, err = conn.Receive(); err != nil {
  307. PromError("redis:增加排行榜主receive")
  308. log.Error("conn.Receive error(%+v)", err)
  309. return
  310. }
  311. }
  312. return
  313. }
  314. // AddCacheHotspotArts .
  315. func (d *Dao) AddCacheHotspotArts(c context.Context, typ int8, id int64, arts [][2]int64, replace bool) (err error) {
  316. var (
  317. key = hotspotKey(typ, id)
  318. conn = d.redis.Get(c)
  319. count int
  320. )
  321. defer conn.Close()
  322. if len(arts) == 0 {
  323. return
  324. }
  325. if replace {
  326. if err = conn.Send("DEL", key); err != nil {
  327. PromError("redis:删除热点标签缓存")
  328. log.Error("conn.Send(DEL, %s) error(%+v)", key, err)
  329. return
  330. }
  331. count++
  332. }
  333. for _, art := range arts {
  334. id := art[0]
  335. score := art[1]
  336. if err = conn.Send("ZADD", key, "CH", score, id); err != nil {
  337. PromError("redis:增加热点标签缓存")
  338. log.Error("conn.Send(ZADD, %s, %d, %v) error(%+v)", key, score, id, err)
  339. return
  340. }
  341. count++
  342. }
  343. if err = conn.Send("EXPIRE", key, d.redisHotspotExpire); err != nil {
  344. PromError("redis:热点标签设定过期")
  345. log.Error("conn.Send(EXPIRE, %s, %d) error(%+v)", key, d.redisHotspotExpire, err)
  346. return
  347. }
  348. count++
  349. if err = conn.Flush(); err != nil {
  350. PromError("redis:增加热点标签缓存flush")
  351. log.Error("conn.Flush error(%+v)", err)
  352. return
  353. }
  354. for i := 0; i < count; i++ {
  355. if _, err = conn.Receive(); err != nil {
  356. PromError("redis:增加热点标签缓存receive")
  357. log.Error("conn.Receive error(%+v)", err)
  358. return
  359. }
  360. }
  361. return
  362. }
  363. // HotspotArtsCache .
  364. func (d *Dao) HotspotArtsCache(c context.Context, typ int8, id int64, start, end int) (res []int64, err error) {
  365. key := hotspotKey(typ, id)
  366. conn := d.redis.Get(c)
  367. defer conn.Close()
  368. res, err = redis.Int64s(conn.Do("ZREVRANGE", key, start, end))
  369. if err != nil {
  370. PromError("redis:获取热点标签列表receive")
  371. log.Error("conn.Send(ZREVRANGE, %s) error(%+v)", key, err)
  372. }
  373. return
  374. }
  375. // HotspotArtsCacheCount .
  376. func (d *Dao) HotspotArtsCacheCount(c context.Context, typ int8, id int64) (res int64, err error) {
  377. key := hotspotKey(typ, id)
  378. conn := d.redis.Get(c)
  379. defer conn.Close()
  380. res, err = redis.Int64(conn.Do("ZCARD", key))
  381. if err != nil {
  382. PromError("redis:获取热点标签计数")
  383. log.Error("conn.Send(ZCARD, %s) error(%+v)", key, err)
  384. }
  385. return
  386. }
  387. // ExpireHotspotArtsCache .
  388. func (d *Dao) ExpireHotspotArtsCache(c context.Context, typ int8, id int64) (ok bool, err error) {
  389. key := hotspotKey(typ, id)
  390. conn := d.redis.Get(c)
  391. defer conn.Close()
  392. if ok, err = redis.Bool(conn.Do("EXPIRE", key, d.redisHotspotExpire)); err != nil {
  393. PromError("redis:热点运营设定过期")
  394. log.Error("conn.Send(EXPIRE, %s) error(%+v)", key, err)
  395. }
  396. return
  397. }
  398. // DelHotspotArtsCache .
  399. func (d *Dao) DelHotspotArtsCache(c context.Context, typ int8, hid int64, aid int64) (err error) {
  400. conn := d.redis.Get(c)
  401. defer conn.Close()
  402. key := hotspotKey(typ, hid)
  403. if _, err = conn.Do("ZREM", key, aid); err != nil {
  404. PromError("redis:删除热点运营文章")
  405. log.Error("conn.Do(ZERM, %s, %d) error(%+v)", key, aid, err)
  406. }
  407. return
  408. }
  409. // AuthorMostCategories .
  410. func (d *Dao) AuthorMostCategories(c context.Context, mid int64) (categories []int64, err error) {
  411. var (
  412. categoriesInts []string
  413. category int64
  414. )
  415. conn := d.redis.Get(c)
  416. defer conn.Close()
  417. key := authorCategoriesKey(mid)
  418. if categoriesInts, err = redis.Strings(conn.Do("SMEMBERS", key)); err != nil {
  419. PromError("redis:获取作者分区")
  420. log.Error("conn.Do(GET, %s) error(%+v)", key, err)
  421. }
  422. for _, categoryInt := range categoriesInts {
  423. if category, err = strconv.ParseInt(categoryInt, 10, 64); err != nil {
  424. PromError("redis:获取作者分区")
  425. log.Error("strconv.Atoi(%s) error(%+v)", categoryInt, err)
  426. return
  427. }
  428. categories = append(categories, category)
  429. }
  430. return
  431. }
  432. // CategoryAuthors .
  433. func (d *Dao) CategoryAuthors(c context.Context, category int64, count int) (authors []int64, err error) {
  434. var (
  435. authorsInts []string
  436. author int64
  437. )
  438. conn := d.redis.Get(c)
  439. defer conn.Close()
  440. key := recommendsAuthorsKey(category)
  441. if authorsInts, err = redis.Strings(conn.Do("SRANDMEMBER", key, count)); err != nil {
  442. PromError("redis:获取分区作者")
  443. log.Error("conn.Do(GET, %s) error(%+v)", key, err)
  444. }
  445. for _, authorInt := range authorsInts {
  446. if author, err = strconv.ParseInt(authorInt, 10, 64); err != nil {
  447. PromError("redis:获取作者分区")
  448. log.Error("strconv.Atoi(%s) error(%+v)", authorInt, err)
  449. return
  450. }
  451. authors = append(authors, author)
  452. }
  453. return
  454. }