本文将使用golang实现两个可以通过postman调用的接口,一个为点击增加热度/播放量接口。一个为获取排行榜接口。为方便起见,将本文章接口将不涉及数据库联动,仅实现简单的ID、热度两个字段。
主要使用Redis中的Zset数据结构和简单的Key Value对。
项目运行环境为Windows10,go1.21.0
Windows下的Redis有很多安装方法,在此给出个人解决方案。
1.通过微软应用商店下载windows terminal
2.通过应用商店下载Ubuntu,通过Ubuntu直接安装即可
Ubuntu安装Redis时具体可以参考Redis官方教程
Gin:本文将使用Gin进行路由注册。官方教程
Go-Redis: golang流行的Redis操作工具之一。官方教程
go get -u github.com/gin-gonic/gin
go get github.com/redis/go-redis/v9
使用go-redis连接至自己的Redis,以下给出使用go-redis的个人初始化方法
package cache
import (
"context"
"os"
"strconv"
"github.com/redis/go-redis/v9"
)
var RedisClient *redis.Client
// init redis
func Redis() {
db, _ := strconv.ParseUint(os.Getenv("REDIS_DB"), 10, 64)
client := redis.NewClient(&redis.Options{
Addr: "127.0.0.1:6379", // 一般Redis服务均使用6379端口
Password: "", // 填入自己的Redis密码默认没有
DB: int(db),
MaxRetries: 1,
})
_, err := client.Ping(context.Background()).Result()
if err != nil {
panic("can't connect redis")
}
RedisClient = client
}
在main.go中注册基本服务和调用连接Redis函数,此处增加热度接口涉及到Gin的路由参数的概念,感兴趣可以查阅官方文档路由参数,如果不想看也没有关系,通过下面的调用示例图,知道有什么用即可。
package main
import (
"fmt"
"rank/cache"
"github.com/gin-gonic/gin"
)
func main() {
cache.Redis() // 连接redis
server := gin.Default()
server.GET("show/:id", func(ctx *gin.Context) {
id := ctx.Param("id")
ctx.JSON(200, fmt.Sprintf("show %s", id))
}) // 将增加热度接口注册在 localhost:3000/show 地址
server.GET("rank", func(ctx *gin.Context) {
ctx.JSON(200, "rank")
}) // 将排行榜注册在 localhost:3000/rank 地址
server.Run(":3000") // 运行在本地3000端口
}
使用go run main.go运行,并尝试使用postman调用一下
成功实现基础接口!!!
增加播放量即调用接口时将Redis中key对应的值+1,排行榜则维护一个Zset(小根堆),在每次调用增加播放量接口时同步增加Zset中的播放量数。
接口实现思路
获取播放量接口时从redis获取对应播放量并增加返回。
获取排行榜接口时直接将Zset中的后十个返回。
由于Redis是键值对存储方式,所以我们需要对Redis存储的Key进行前缀定义,方便管理以及后续操作
package cache
import "fmt"
const (
// DailyRankKey 排行榜的zset key
DailyRankKey = "redis-test-rank:daily"
)
// ShareKey 为每个ID加上指定前缀
func ShareKey(id string) string {
return fmt.Sprintf("redis-test-share:%s", id)
}
package main
import (
"context"
"rank/cache"
"strconv"
)
type Share struct {
Id string
ViewCount int64
}
// 获取播放量函数
func (share *Share) GetViewCount() (num int64) {
countStr, _ := cache.RedisClient.Get(context.Background(), cache.ShareKey(share.Id)).Result()
if countStr == "" {
return 0
}
num, _ = strconv.ParseInt(countStr, 10, 64)
return
}
// AddViewCount 增加播放量函数
func (share *Share) AddViewCount() {
// 增加播放量
cache.RedisClient.Incr(context.Background(), cache.ShareKey(share.Id))
// 增加在排行榜中的播放量
cache.RedisClient.ZIncrBy(context.Background(), cache.DailyRankKey, 1, share.Id)
}
func ShowViewCount(ctx *gin.Context) {
id := ctx.Param("id")
share := Share{
Id: id,
}
// 增加播放量
share.AddViewCount()
// 获取Redis数据
share.ViewCount = share.GetViewCount()
ctx.JSON(200, share)
}
接口演示
func GetRank(ctx *gin.Context) {
shares := make([]Share, 0, 16)
// 从Redis中获取排行榜
shareRank, err := cache.RedisClient.ZRevRange(context.Background(), cache.DailyRankKey, 0, 9).Result()
if err != nil {
ctx.JSON(200, err.Error())
return
}
// 获取排行榜内对应排名的播放量
if len(shareRank) > 0 {
for _, shareId := range shareRank {
share := Share{
Id: shareId,
}
share.ViewCount = share.GetViewCount()
shares = append(shares, share)
}
}
// 填充空的排行榜排名至十个
emptyShare := Share{
Id: "虚位以待",
ViewCount: 0,
}
for len(shares) < 10 {
shares = append(shares, emptyShare)
}
// 由于获取排行榜时有可能排行榜的Zset发生变动,需要按照确定的播放数重新排名一次
sort.Slice(shares, func(i, j int) bool {
return shares[i].ViewCount > shares[j].ViewCount
})
ctx.JSON(200, shares)
}
调用接口演示
main.go
package main
import (
"context"
"rank/cache"
"sort"
"github.com/gin-gonic/gin"
)
func ShowViewCount(ctx *gin.Context) {
id := ctx.Param("id")
share := Share{
Id: id,
}
// 增加播放量
share.AddViewCount()
// 获取Redis数据
share.ViewCount = share.GetViewCount()
ctx.JSON(200, share)
}
func GetRank(ctx *gin.Context) {
shares := make([]Share, 0, 16)
// 从Redis中获取排行榜
shareRank, err := cache.RedisClient.ZRevRange(context.Background(), cache.DailyRankKey, 0, 9).Result()
if err != nil {
ctx.JSON(200, err.Error())
return
}
// 获取排行榜内对应排名的播放量
if len(shareRank) > 0 {
for _, shareId := range shareRank {
share := Share{
Id: shareId,
}
share.ViewCount = share.GetViewCount()
shares = append(shares, share)
}
}
// 填充空的排行榜排名至十个
emptyShare := Share{
Id: "虚位以待",
ViewCount: 0,
}
for len(shares) < 10 {
shares = append(shares, emptyShare)
}
// 由于获取排行榜时有可能排行榜的Zset发生变动,需要按照确定的播放数重新排名一次
sort.Slice(shares, func(i, j int) bool {
return shares[i].ViewCount > shares[j].ViewCount
})
ctx.JSON(200, shares)
}
func main() {
cache.Redis() // 连接redis
server := gin.Default()
server.GET("show/:id", ShowViewCount) // 将增加播放量接口注册在 localhost:3000/show 地址
server.GET("rank", GetRank) // 将排行榜注册在 localhost:3000/rank 地址
server.Run(":3000") // 运行在本地3000端口
}
share.go
package main
import (
"context"
"rank/cache"
"strconv"
)
type Share struct {
Id string
ViewCount int64
}
// 获取播放量函数
func (share *Share) GetViewCount() (num int64) {
countStr, _ := cache.RedisClient.Get(context.Background(), cache.ShareKey(share.Id)).Result()
if countStr == "" {
return 0
}
num, _ = strconv.ParseInt(countStr, 10, 64)
return
}
// AddViewCount 增加播放量函数
func (share *Share) AddViewCount() {
// 增加播放量
cache.RedisClient.Incr(context.Background(), cache.ShareKey(share.Id))
// 增加在排行榜中的播放量
cache.RedisClient.ZIncrBy(context.Background(), cache.DailyRankKey, 1, share.Id)
}
cache/cache.go
package cache
import (
"context"
"os"
"strconv"
"github.com/redis/go-redis/v9"
)
var RedisClient *redis.Client
// init redis
func Redis() {
db, _ := strconv.ParseUint(os.Getenv("REDIS_DB"), 10, 64)
client := redis.NewClient(&redis.Options{
Addr: os.Getenv("REDIS_ADDR"),
Password: os.Getenv("REDIS_PW"),
DB: int(db),
MaxRetries: 1,
})
_, err := client.Ping(context.Background()).Result()
if err != nil {
panic("can't connect redis")
}
RedisClient = client
}
cache/key.go
package cache
import "fmt"
const (
// DailyRankKey 排行榜的zset key
DailyRankKey = "redis-test-rank:daily"
)
// ShareKey 为每个ID加上指定前缀
func ShareKey(id string) string {
return fmt.Sprintf("redis-test-share:%s", id)
}
如果你想学习排行榜与数据库之间的联动以及更复杂的Redis使用方法,可以参考我的项目https://github.com/ChenMiaoQiu/go-cloud-disk欢迎提Issue促进我的项目变得更好!
如果有更多疑问,可以在评论区留言。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。