redis发布订阅注意事项

redis发布订阅注意事项



前言

Redis 的发布/订阅(Pub/Sub)是一个消息传递模式,主要有发布端和订阅端两个角色,发送者(pub)发送消息,订阅者(sub)接收消息。这种机制可以用于实时消息推送、事件通知等应用场景。

今天我在用redis的发布/订阅模式,发现订阅端在接收消息时一直报错,所以简单记录下这次错误。


一、报错现场还原

redis配置

// redis_util.js
var redis = require('redis');
var client = redis.createClient(6379,'127.0.0.1');

发布端代码

// publish_clien.js
const redis = require('redis_utils');
priority = 1
await redis.zadd('manager_queue', priority, JSON.stringify(job_data));
redis.publish('taskManagerChannel', 'New task added');

订阅端代码

const redis = require('redis_utils');
function subscribeToChannel(channel) {
    // 订阅频道 subscribe channel
    redis.subscribe(channel, (err, count) => {
        if (err) {
            console.error('Job-Manager',Failed to subscribe: ${err}.);
            return;
        }
        console.log('Job-Manager', Subscribed to ${count} channel(s).);
    });

    // 监听消息 listen message
    redis.on('message', async (channel, message) => {
        if (message === 'New task added') {
            console.log('Job-Manager', Received message from channel ${channel}: ${message});

            const jb = await imageQueue.getJobCounts();
            const total_Counts = jb.completed + jb.failed + jb.waiting + jb.active + jb.delayed + jb.paused;
            const completed_Counts = await imageQueue.getCompletedCount();
            const failed_Counts = await imageQueue.getFailedCount();

            if (total_Counts === completed_Counts + failed_Counts) {
                const result = await redis.zrange(redis.manager_queue, 0, 0);
                if (result.length > 0) {
                    await imageQueue.add(JSON.parse(result[0]));
                    await redis.zrem('manager_queue', result[0]);
                }
            }

        }

    });
}
subscribeToChannel('taskManagerChannel');

上述代码出了问题

2024-08-05 16:08:39 ---> <任务管理调度> : Subscribed to taskManagerChannel channel(s).
2024-08-05 16:08:54 ---> <任务管理调度> : Received message from channel taskManagerChannel: New task added
Redis Error ReplyError: ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / PING / QUIT allowed in this context

从上面控制台可以看到,redis抛出了一个ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / PING / QUIT allowed in this context,这是什么意思呢?

Redis 客户端在订阅模式下只能执行订阅相关的命令(如 SUBSCRIBEUNSUBSCRIBEPINGQUIT),不允许执行其他操作(如 SETGET。如果我们在处理订阅消息时使用了相同的客户端实例来执行其他 Redis 命令,这可能会导致该错误。

很明显,我们的订阅端代码在操作redis的有序集合时所用到的redis实例和订阅监听所用到的redis实例是同一个,这就违反了**Redis 客户端在订阅模式下只能执行订阅相关的命令(如 SUBSCRIBEUNSUBSCRIBEPINGQUIT),不允许执行其他操作(如 SETGET)**。,所以我们的代码出现了Redis Error ReplyError: ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / PING / QUIT allowed in this context这个错误

await redis.zrem('manager_queue', result[0]);`
const result = await redis.zrange(redis.manager_queue, 0, 0);
==================================================================
redis.subscribe(channel, (err, count)
redis.on('message', async (channel, message)

二、代码修正

redis配置文件

//redis_utils.js
var redis = require('redis');
var subscriberClient = redis.createClient(6379, '127.0.0.1'); // 订阅客户端
var publisherClient = redis.createClient(6379, '127.0.0.1'); // 发布客户端

subscriberClient.on('ready', function () {
  console.log('Redis subscriber client: ready');
});

publisherClient.on('ready', function () {
  console.log('Redis publisher client: ready');
});

module.exports = {
    getSubscriberClient: () => subscriberClient,
    getPublisherClient: () => publisherClient,
    // Other methods as needed
};
// 然后,发布代码中使用publisherClient,订阅代码中使用subscriberClient
// 注意 在 redisClient.on('message') 回调中只处理消息,而不进行其他 Redis 操作。如果需要执行 Redis 操作,请使用单独的客户端实例。

订阅端

const redis = require('../database/redis-client');

const publisherClient = redis.getPublisherClient();
const subscriberClient = redis.getSubscriberClient();

function subscribeToChannel(channel) {
    subscriberClient.subscribe(channel, (err, count) => {  // 这里改成了subscriberClient
        if (err) {
            console.error('Job-Manager',Failed to subscribe: ${err}.);
            return;
        }
        console.log('Job-Manager', Subscribed to ${count} channel(s).);
    });

    subscriberClient.on('message', async (channel, message) => {  //这里改成了subscriberClient
        if (message === 'New task added') {
            console.log('Job-Manager', Received message from channel ${channel}: ${message});

            const jb = await imageQueue.getJobCounts();
            const total_Counts = jb.completed + jb.failed + jb.waiting + jb.active + jb.delayed + jb.paused;
            const completed_Counts = await imageQueue.getCompletedCount();
            const failed_Counts = await imageQueue.getFailedCount();

            if (total_Counts === completed_Counts + failed_Counts) {
                const result = await publisherClient.zrange(redis.manager_queue, 0, 0); //这里做了改动
                if (result.length > 0) {
                    var job_opts = {removeOnComplete: 10, removeOnFail: {age: 60*60*24}};
                    await imageQueue.add(JSON.parse(result[0]), job_opts);
                    await publisherClient.zrem('manager_queue', result[0]); //这里做了改动
                }
            }

        }

    });
}

subscribeToChannel('taskManagerChannel');```

---


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hola173841439

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值