如何实现
利用publish发送消息之后,将消息储存在列表中,
确保用户在subscribe之后能看见之前的消息,
用户在订阅时最多只能看见10条消息,
更早发送的消息会被弹出列表。
在用户订阅频道时,将储存的消息遍历,写在缓存区,随后输出在web界面
代码实现
dao.go
package dao
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
"log"
)
// 定义一个上下文变量
var ctx context.Context
// 定义一个 Redis 客户端
var rdb *redis.Client
// init 函数用于初始化 Redis 客户端和上下文
func init() {
// 创建一个背景上下文
ctx = context.Background()
// 初始化 Redis 客户端配置
rdb = redis.NewClient(&redis.Options{
Addr: "ip", // Redis 服务器的 IP 地址
Password: "password", // Redis 服务器的密码,如果没有可以留空
DB: 0, // 默认使用的数据库索引
})
}
// Publish 函数用于向指定频道发布消息
func Publish(channel string, message string) bool {
// 构建消息列表的键名
mes := fmt.Sprintf("mess:%v", channel)
// 将消息推入列表的左侧
err := rdb.LPush(ctx, mes, message).Err()
if err != nil {
log.Println(err) // 如果出现错误,则记录日志
return false // 返回 false 表示发布失败
}
// 将消息发布到指定频道
err2 := rdb.Publish(ctx, channel, message).Err()
if err2 != nil {
log.Println(err2) // 如果出现错误,则记录日志
return false // 返回 false 表示发布失败
}
// 返回 true 表示消息成功发布
return true
}
// Subscribe 函数用于获取指定频道的消息
func Subscribe(channel string) []string {
// 构建消息列表的键名
mes := fmt.Sprintf("mess:%v", channel)
// 从列表中获取所有消息
message, err := rdb.LRange(ctx, mes, 0, -1).Result()
if err != nil {
log.Println(err) // 如果出现错误,则记录日志
return nil // 返回 nil 表示获取消息失败
}
// 如果消息数量超过 10,则从尾部移除多余的消息
for len(message) >= 10 {
// 从列表中移除最后一个消息
err := rdb.RPop(ctx, mes).Err()
if err != nil {
log.Println(err) // 如果出现错误,则记录日志
return nil // 返回 nil 表示获取消息失败
}
// 重新获取当前消息列表
message, err = rdb.LRange(ctx, mes, 0, -1).Result()
if err != nil {
log.Println(err) // 如果出现错误,则记录日志
return nil // 返回 nil 表示获取消息失败
}
}
// 返回当前所有消息
return message
}
api.go
package api
import (
"Golang/12December/20241211/Subscribe/dao"
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
// PUBLISH 函数处理发布消息的 HTTP 请求
func PUBLISH(c *gin.Context) {
// 从 POST 请求中获取消息和频道参数
message := c.PostForm("message")
channel := c.PostForm("channel")
// 调用 dao 层的 Publish 方法尝试发送消息
if !dao.Publish(channel, message) {
// 如果发送失败,返回 400 错误响应
c.JSON(http.StatusBadRequest, gin.H{
"code": 400,
"message": "发送失败",
})
return
}
// 构建成功响应消息
mes := fmt.Sprintf("你成功发送了消息:%s ,在频道%s", message, channel)
// 返回 200 响应,包含成功消息
c.JSON(http.StatusOK, gin.H{
"code": 200,
"message": mes,
})
return
}
// SUBSCRIBE 函数处理订阅频道的 HTTP 请求
func SUBSCRIBE(c *gin.Context) {
// 从 POST 请求中获取频道参数
channel := c.PostForm("channel")
// 调用 dao 层的 Subscribe 方法获取指定频道的消息
mes := dao.Subscribe(channel)
// 如果没有获取到任何消息,返回 400 错误响应
if mes == nil {
c.JSON(http.StatusBadRequest, gin.H{
"code": 400,
"message": "无任何消息",
})
return
}
var allMsg string
// 遍历获取到的消息,构建返回的消息字符串
for _, msg := range mes {
allMsg += fmt.Sprintf("对方发布了消息:%v ||||||", msg) // 使用 |||||| 分隔多个消息
}
// 返回 200 响应,包含所有消息
c.JSON(http.StatusOK, gin.H{
"code": 200,
"message": allMsg,
})
}
api1.go
package api
import "github.com/gin-gonic/gin"
// InitRouter 函数初始化 Gin 路由并配置 API 路由
func InitRouter() {
// 创建一个默认的 Gin 路由实例
r := gin.Default()
// 配置 POST 请求的路由
r.POST("publish", PUBLISH) // 当请求到 /publish 时,调用 PUBLISH 函数处理请求
r.POST("subscribe", SUBSCRIBE) // 当请求到 /subscribe 时,调用 SUBSCRIBE 函数处理请求
// 启动 HTTP 服务器,监听在 8080 端口
r.Run(":8080") // 阻塞并开始服务,直到手动停止
}
最后是main.go
package main
import "Golang/12December/20241211/Subscribe/api"
func main() {
// 调用 api 包中的 InitRouter 函数,初始化路由并启动 Gin HTTP 服务器
api.InitRouter()
}
结语
这段代码实现了一个简单的订阅/发布系统,虽然总的来说功能并不完善,可以在此基础上扩展其他功能,例如增强错误处理、实现用户身份验证、增加日志记录等等,这里就不做过多演示了