Redis2.6 - publish/subscribe的BUG

前一段时间由于业务需要,我们需要搬迁服务器到新机房。为了保证系统平滑过渡,我们分成多批搬迁。迁移工作总体还算顺利,期间也遇到了一点点小挫折。下面将介绍一个我印象最为深刻的BUG:redis2.6的publish/subscribe。

进入主题,先介绍下业务场景。

业务场景

我们生产环境使用的redis是2.6版本,redis集群只有两台服务器,一主(A)一从(B)。
制定的redis迁移方案:
1. 先将redis从服务器迁移到新机房;
2. 迁移从服务器完毕后,启动从服务器同步数据。待完成同步数据后,进行主从切换(即从->主,主->从);
3. 最后将redis主节点连接配置解析到新IP。

现有两个服务使用publish/subscribe方式进行通信,publish和subscribe端连接的都是主节点。在上述步骤完成后且未对A节点重启的情况下,subscribe端的服务始终无法接收到publish端推送的消息。

问题分析

通过查看服务日志及追踪,可以排除服务自身的问题。那么问题可能出现在redis。

由于subscribe使用的是长连接,当主从切换后,在原主服务器未重启的情况下,subscribe依旧连接的是A服务器,publish端则连接的是B服务器。难道在这种情况下B服务器收到publish命令后不会往A广播消息?

印象中记得主服务器收到publish命令后会将消息广播到集群其他节点。为了消除猜疑,于是本地模拟了一遍上述流程(注:redis3.0版本),结果显示subscribe端能正常收到消息。

时间一分一秒的过去,由于两个机房有一段的距离,没有办法做到快速回滚。于是拉上其他同事一起讨论。

经过一番讨论和分析,最终我们认为问题出在redis方面的可能性最大。

由于线上的redis版本是2.6,于是从官网下载了一份redis-2.6源码。在pubsub.c文件找到publishCommand代码:

void publishCommand(redisClient *c) {
    int receivers = pubsubPublishMessage(c->argv[1],c->argv[2]);
    addReplyLongLong(c,receivers);
}

pubsubPublishMessage函数的功能:向订阅该频道的客户端推送消息。addReplyLongLong函数的功能:向客户端返回订阅该消息的客户端数。

果不其然,在redis2.6版本中,并没有向其集群的其他节点广播消息。

我们再看看redis-3.0是如何实现?
publishCommand代码:

void publishCommand(redisClient *c) {

    int receivers = pubsubPublishMessage(c->argv[1],c->argv[2]);
    if (server.cluster_enabled)
        clusterPropagatePublish(c->argv[1],c->argv[2]);
    else
        forceCommandPropagation(c,REDIS_PROPAGATE_REPL);
    addReplyLongLong(c,receivers);
}

与redis2.6源码对比,redis3.0的publishCommand命令内部多执行了一步。
在redis-cluster模式下系统会调用clusterPropagatePublish; 而我们的生产环境只是主从模式,则调用forceCommandPropagation函数。

void forceCommandPropagation(redisClient *c, int flags) {
    if (flags & REDIS_PROPAGATE_REPL) c->flags |= REDIS_FORCE_REPL;
    if (flags & REDIS_PROPAGATE_AOF) c->flags |= REDIS_FORCE_AOF;
}

这里给客户端增加REDIS_PROPAGATE_REPL标识。

当执行完publishCommand命令后,redis会继续执行propagate函数(注:call内部调用)。

void propagate(struct redisCommand *cmd, int dbid, robj **argv, int argc,int flags)
{
    //...省略部分代码...
    // 传播到 slave
    if (flags & REDIS_PROPAGATE_REPL)
        replicationFeedSlaves(server.slaves,dbid,argv,argc);
}

此时,系统会执行replicationFeedSlaves函数。replicationFeedSlaves的功能就是向当前redis节点的从节点广播消息。

以上就是redis3.0的主节点的publishCommand向从节点广播流程。

总结

关于redis2.6版本的publish/subscribeBUG,在3.0版本已经修复,建议升级到3.0以上版本。
对于这个BUG的处理,我们可以重启相应的应用或者主从切换后重启原主节点。当然,我们也可以在应用监控“主从切换”状态判断是否需要重连。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值