Golang---利用redis中的PUBLISH和SUBSCRIBE命令实现简单的发布/订阅的操作

如何实现

利用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()
}

结语

这段代码实现了一个简单的订阅/发布系统,虽然总的来说功能并不完善,可以在此基础上扩展其他功能,例如增强错误处理、实现用户身份验证、增加日志记录等等,这里就不做过多演示了

-----END------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值