理解了go-redis订阅发送功能在同一个文件使用匿名文件为什么接收端时有时无问题
今天晚上卡在这个部分一天了,问chatgpt,搜索各种软件。
发现问题
发现这个原因是在学习go-redis的订阅发送信息的时候,gpt给了这样一个例子:
package main
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
func main() {
// 创建Redis客户端
client := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
ctx := context.Background()
// 订阅频道
go func() {
sub := client.Subscribe(ctx, "channel1", "channel2")
defer sub.Close()
for {
msg, err := sub.ReceiveMessage(ctx)
if err != nil {
// 处理错误
}
// 处理接收到的消息
fmt.Printf("Received message from channel %s: %s\n", msg.Channel, msg.Payload)
}
}()
// 发布消息
err := client.Publish(ctx, "channel1", "Hello, subscribers!").Err()
if err != nil {
// 处理错误
}
}
运行发现始终出现不了
但是重新删除配置,再次运行
然后点击运行,此时会得到 我想要的信息
再次点击又会出现第一次没有信息的情况,需要重新配置,成死循环了,晕
解决过程
-
持续问机器人,为什么会出现这种问题,然后它回答道:
但是笔者感觉不对,因为上述代码逻辑是:订阅连接的关闭操作发生在订阅 goroutine 结束时,而客户端连接的关闭操作发生在
main
函数即将结束时。唯一出现出现上述结果的就是,main函数和订阅的goroutine协程运行速度不同,造成订阅消息的携程还没有接受到消息就main函数结束,整个程序结束,无法打印出消息。
-
在main函数中设置time.sleep
最先我是在
err := client.Publish(ctx, "channel1", "Hello, subscribers!").Err()
一行前方设置了time.Sleep(5 * time.Second)
来保证订阅消息的订阅。但是结果只是 停了5秒依旧没有打印。
明白了:main函数中的发送在订阅goroutine中还没有收到就结束了进程
-
增设time.sleep
这一次我在
err := client.Publish(ctx, "channel1", "Hello, subscribers!").Err()
后面设置了time.Sleep(2* time.Second)
所以说,这就是不同进程之间的通信协作问题没有解决。
解决方法
解决通信问题,让发送进程等接受进程成功打印后再退出
我这里写几个我时间出来的解决结果
法一:使用time.sleep()函数
可以看一下上一个目录章节–解决过程中最后部分。通过人工设置几秒让main中的发送等待订阅goroutine的接受。
func main() {
// 创建Redis客户端
client := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
ctx := context.Background()
// 订阅频道
go func() {
sub := client.Subscribe(ctx, "channel1", "channel2")
defer sub.Close()
for {
msg, err := sub.ReceiveMessage(ctx)
if err != nil {
// 处理错误
}
// 处理接收到的消息
fmt.Printf("Received message from channel %s: %s\n", msg.Channel, msg.Payload)
}
}()
time.Sleep(5 * time.Second)
// 发布消息
err := client.Publish(ctx, "channel1", "Hello, subscribers!").Err()
if err != nil {
// 处理错误
}
time.Sleep(2 * time.Second)
}
法二:互换main函数和goroutine的功能
让main函数进行订阅接受信息并打印,goroutine中进行发送,实现速度的匹配。
但是这样有一个缺点就是,主进程结束不了,一直在for循环中等待,可以使用time.after()函数来关闭进程。
func main() {
// 创建Redis客户端
client := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
ctx := context.Background()
// 订阅频道
go func() {
// 发布消息
err := client.Publish(ctx, "channel1", "Hello, subscribers!").Err()
if err != nil {
// 处理错误
}
}()
sub := client.Subscribe(ctx, "channel1", "channel2")
for {
msg, err := sub.ReceiveMessage(ctx)
if err != nil {
// 处理错误
}
// 处理接收到的消息
fmt.Printf("Received message from channel %s: %s\n", msg.Channel, msg.Payload)
}
}
法三:直接在另一个包中新建文件实现接受功能
直接新建一个文件,在该文件的main中接受信息打印,其实质就是在上述主函数中用goroutine实现结果,在这就不赘述了。大家啊可以动手实践一下。
法四:使用WaitGroup和mutex实现通信
因为WaitGroup就是go中用来通信的工具包,可以使用来实现我们的结果。或者运用channel来实现。理论上是可以实现的,笔者也在之前实现过(不小心删了,大家自行实践一下