[英]Unsubscribe from Redis doesn't seem to work
我正在嘗試在 Redis 中使用 pub-sub。 我要做的是打開兩個redis-cli
。 第一個我發出命令flushall
以確保以綠色啟動。
然后在另一個終端中,我打開MONITOR以打印來自 Golang 示例客戶端的所有命令(代碼如下)。
這是我從 MONITOR 打印的內容:
1590207069.340860 [0 127.0.0.1:58910] "smembers" "user:Ali:rooms"
1590207069.341380 [0 127.0.0.1:58910] "sadd" "user:Ali:rooms" "New"
1590207069.345266 [0 127.0.0.1:58910] "smembers" "user:Ali:rooms"
1590207069.353706 [0 127.0.0.1:58910] "sadd" "user:Ali:rooms" "Old"
1590207069.354219 [0 127.0.0.1:58912] "subscribe" "New"
1590207069.354741 [0 127.0.0.1:58910] "smembers" "user:Ali:rooms"
1590207069.355444 [0 127.0.0.1:58912] "unsubscribe" "New" "Old"
1590207069.356754 [0 127.0.0.1:58910] "sadd" "user:Ali:rooms" "OldPlusPlus"
1590207069.357206 [0 127.0.0.1:58914] "subscribe" "New" "Old"
1590207069.357656 [0 127.0.0.1:58910] "smembers" "user:Ali:rooms"
1590207069.358362 [0 127.0.0.1:58912] "unsubscribe" "OldPlusPlus" "New" "Old"
1590207069.361030 [0 127.0.0.1:58916] "subscribe" "OldPlusPlus" "New" "Old"
我試圖讓客戶端對隨着時間的推移打開的所有通道都有一個連接。 而不是一個連接/線程來處理每個通道到 Redis。 因此,每當需要新的訂閱請求時,我都會嘗試從客戶端刪除所有以前的訂閱,並對舊頻道和新頻道進行新訂閱。
但似乎unsubscribe
命令沒有按預期工作(或者我遺漏了一些東西)!
因為當我嘗試獲取每個頻道的客戶端數量時,從第一個終端:
127.0.0.1:6379> pubsub numsub OldPlusPlus New Old
1) "OldPlusPlus"
2) (integer) 1
3) "New"
4) (integer) 2
5) "Old"
6) (integer) 2
除了當我嘗試向“新”頻道發送消息時,我的 go 客戶端收到了兩次消息。
這是生成上述命令的代碼:
package main
import (
"fmt"
"github.com/go-redis/redis/v7"
"log"
)
type user struct {
name string
rooms []string
endSub chan bool
sub bool
}
func (u *user) connect(rdb *redis.Client) error {
// get all user rooms (from DB) and start subscribe
r, err := rdb.SMembers(fmt.Sprintf("user:%s:rooms", u.name)).Result()
if err != nil {
return err
}
u.rooms = r
if len(u.rooms) == 0 {
return nil
}
u.doSubscribe(rdb)
return nil
}
func (u *user) subscribe(room string, rdb *redis.Client) error {
// check if already subscribed
for i := range u.rooms {
if u.rooms[i] == room {
return nil
}
}
// add room to user
userRooms := fmt.Sprintf("user:%s:rooms", u.name)
if err := rdb.SAdd(userRooms, room).Err(); err != nil {
return err
}
// get all user rooms (from DB) and start subscribe
r, err := rdb.SMembers(userRooms).Result()
if err != nil {
return err
}
u.rooms = r
if u.sub {
u.endSub <- true
}
u.doSubscribe(rdb)
return nil
}
func (u *user) doSubscribe(rdb *redis.Client) {
sub := rdb.Subscribe(u.rooms...)
go func() {
u.sub = true
fmt.Println("starting the listener for user:", u.name, "on rooms:", u.rooms)
for {
select {
case msg, ok := <-sub.Channel():
if !ok {
break
}
fmt.Println(msg.Payload)
case <-u.endSub:
fmt.Println("Stop listening for user:", u.name, "from rooms:", u.rooms)
if err := sub.Unsubscribe(u.rooms...); err != nil {
fmt.Println("error unsubscribing")
return
}
break
}
}
}()
}
func (u *user) unsubscribe(room string, rdb *redis.Client) error {
return nil
}
var rdb *redis.Client
func main() {
rdb = redis.NewClient(&redis.Options{Addr: "localhost:6379"})
u := &user{
name: "Ali",
endSub: make(chan bool),
}
if err := u.connect(rdb); err != nil {
log.Fatal(err)
}
if err := u.subscribe("New", rdb); err != nil {
log.Fatal(err)
}
if err := u.subscribe("Old", rdb); err != nil {
log.Fatal(err)
}
if err := u.subscribe("OldPlusPlus", rdb); err != nil {
log.Fatal(err)
}
select {}
}
該問題與訂閱該頻道的 *redis.PubSub 類型的 object 不是用於取消訂閱該頻道的那個有關。
因此,我必須維護對此類 object 的引用,然后使用該引用取消訂閱所有頻道。
這是修改和工作的代碼:
package main
import (
"fmt"
"github.com/go-redis/redis/v7"
"log"
)
type user struct {
name string
rooms []string
stopRunning chan bool
running bool
roomsPubsub map[string]*redis.PubSub
}
func (u *user) connect(rdb *redis.Client) error {
// get all user rooms (from DB) and start subscribe
r, err := rdb.SMembers(fmt.Sprintf("user:%s:rooms", u.name)).Result()
if err != nil {
return err
}
u.rooms = r
if len(u.rooms) == 0 {
return nil
}
u.doSubscribe("", rdb)
return nil
}
func (u *user) subscribe(room string, rdb *redis.Client) error {
// check if already subscribed
for i := range u.rooms {
if u.rooms[i] == room {
return nil
}
}
// add room to user
userRooms := fmt.Sprintf("user:%s:rooms", u.name)
if err := rdb.SAdd(userRooms, room).Err(); err != nil {
return err
}
// get all user rooms (from DB) and start subscribe
r, err := rdb.SMembers(userRooms).Result()
if err != nil {
return err
}
u.rooms = r
if u.running {
u.stopRunning <- true
}
u.doSubscribe(room, rdb)
return nil
}
func (u *user) doSubscribe(room string, rdb *redis.Client) {
pubSub := rdb.Subscribe(u.rooms...)
if len(room) > 0 {
u.roomsPubsub[room] = pubSub
}
go func() {
u.running = true
fmt.Println("starting the listener for user:", u.name, "on rooms:", u.rooms)
for {
select {
case msg, ok := <-pubSub.Channel():
if !ok {
break
}
fmt.Println(msg.Payload, msg.Channel)
case <-u.stopRunning:
fmt.Println("Stop listening for user:", u.name, "on old rooms")
for k, v := range u.roomsPubsub {
if err := v.Unsubscribe(); err != nil {
fmt.Println("unable to unsubscribe", err)
}
delete(u.roomsPubsub, k)
}
break
}
}
}()
}
func (u *user) unsubscribe(room string, rdb *redis.Client) error {
return nil
}
var rdb *redis.Client
func main() {
rdb = redis.NewClient(&redis.Options{Addr: "localhost:6379"})
u := &user{
name: "Wael",
stopRunning: make(chan bool),
roomsPubsub: make(map[string]*redis.PubSub),
}
if err := u.connect(rdb); err != nil {
log.Fatal(err)
}
if err := u.subscribe("New", rdb); err != nil {
log.Fatal(err)
}
if err := u.subscribe("Old", rdb); err != nil {
log.Fatal(err)
}
if err := u.subscribe("OldPlusPlus", rdb); err != nil {
log.Fatal(err)
}
select {}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.