簡體   English   中英

退訂 Redis 似乎不起作用

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM