redis 主从超时检测

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介:  redis的主从超时检测主要从以下三个方面进行判断,分别是主监测从、从监测主、正常关闭。主监测从:slave定期发送replconf ack offset命令到master来报告自己的存活状况从监测主:master定期发送ping命令或者\n命...

 redis的主从超时检测主要从以下三个方面进行判断,分别是主监测从、从监测主、正常关闭。

  • 主监测从:slave定期发送replconf ack offset命令到master来报告自己的存活状况
  • 从监测主:master定期发送ping命令或者\n命令到slave来报告自己的存货状况
  • 正常关闭:eventLoop监测端口关闭的nio事件


周期性发送心跳命令

 定期执行函数serverCron内部会周期性的执行replicationCron方法,该方法内部执行的动作包括重连接主服务器、向主服务器发送 ACK 、判断数据发送失败情况、断开本服务器超时的从服务器,向从服务器发送PING或者\n命令

int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
    clientsCron();

    // 对数据库执行各种操作
    databasesCron();

    // 重连接主服务器、向主服务器发送 ACK 、判断数据发送失败情况、断开本服务器超时的从服务器,等等
    run_with_period(1000) replicationCron();

    // 增加 loop 计数器
    server.cronloops++;

    return 1000/server.hz;
}



 replicationCron内部做的事情就是周期性的发送心跳命令包括:

  • slave发往master的replconf ack offset,用于主检测从的存活性
  • master发往slave的PING命令,用于从检测主的存活性
  • master发往slave的\n命令,用于从检测主的存活性
// 复制 cron 函数,每秒调用一次
void replicationCron(void) {
        
        // 定期向主服务器发送 ACK 命令
        if (server.masterhost && server.master &&
        !(server.master->flags & REDIS_PRE_PSYNC))
        replicationSendAck();
    
    /* 
     * 如果服务器有从服务器,定时向它们发送 PING 。
     *
     * 这样从服务器就可以实现显式的 master 超时判断机制,
     * 即使 TCP 连接未断开也是如此。
     */
    if (!(server.cronloops % (server.repl_ping_slave_period * server.hz))) {
        listIter li;
        listNode *ln;
        robj *ping_argv[1];

        /* First, send PING */
        // 向所有已连接 slave (状态为 ONLINE)发送 PING
        ping_argv[0] = createStringObject("PING",4);
        replicationFeedSlaves(server.slaves, server.slaveseldb, ping_argv, 1);
        decrRefCount(ping_argv[0]);

        /*
         * 向那些正在等待 RDB 文件的从服务器(状态为 BGSAVE_START 或 BGSAVE_END)
         * 发送 "\n"
         *
         * 这个 "\n" 会被从服务器忽略,
         * 它的作用就是用来防止主服务器因为长期不发送信息而被从服务器误判为超时
         */
        listRewind(server.slaves,&li);
        while((ln = listNext(&li))) {
            redisClient *slave = ln->value;

            if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_START ||
                slave->replstate == REDIS_REPL_WAIT_BGSAVE_END) {
                if (write(slave->fd, "\n", 1) == -1) {
                    /* Don't worry, it's just a ping. */
                }
            }
        }
    }
}


主监测从

 master收到slave的replconf命令的时候更新c->repl_ack_time,也就是代表收到slave发送ack命令的时间。

/* REPLCONF <option> <value> <option> <value> ...
 * 由 slave 使用,在 SYNC 之前配置复制进程(process)
 * 目前这个函数的唯一作用就是,让 slave 告诉 master 它正在监听的端口号
 * 然后 master 就可以在 INFO 命令的输出中打印这个号码了。
 * 将来可能会用这个命令来实现增量式复制,取代 full resync 。
 */
void replconfCommand(redisClient *c) {
    for (j = 1; j < c->argc; j+=2) {
      else if (!strcasecmp(c->argv[j]->ptr,"ack")) {
            // 更新最后一次发送 ack 的时间
            c->repl_ack_time = server.unixtime;
           
            return;
        } 
    }
    addReply(c,shared.ok);
}



 master从判断当前时间和上一次ack时间来判断slave的存,(server.unixtime - slave->repl_ack_time) > server.repl_timeout。如果超时就释放和slave的连接。

// 复制 cron 函数,每秒调用一次
void replicationCron(void) {
    // 断开超时从服务器
    if (listLength(server.slaves)) {
        listIter li;
        listNode *ln;

        // 遍历所有从服务器
        listRewind(server.slaves,&li);
        while((ln = listNext(&li))) {
            redisClient *slave = ln->value;
            // 释放超时从服务器
            if ((server.unixtime - slave->repl_ack_time) > server.repl_timeout)
            {
                char ip[REDIS_IP_STR_LEN];
                int port;

                if (anetPeerToString(slave->fd,ip,sizeof(ip),&port) != -1) {
                    redisLog(REDIS_WARNING,
                        "Disconnecting timedout slave: %s:%d",
                        ip, slave->slave_listening_port);
                }
                
                // 释放
                freeClient(slave);
            }
        }
    }


从监测主

 slave每次接收到master发送过来的命令的时候都会更新client的上一次交互时间也就是c->lastinteraction,这里的client c代表就是slave连接master的server.master的redis client对象。


/*
 * 读取客户端的查询缓冲区内容
 */
void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
    
    // 读入内容到查询缓存
    nread = read(fd, c->querybuf+qblen, readlen);

    if (nread) {
        // 记录服务器和客户端最后一次互动的时间
        c->lastinteraction = server.unixtime;
    } 

    // 从查询缓存重读取内容,创建参数,并执行命令
    // 函数会执行到缓存中的所有内容都被处理完为止
    processInputBuffer(c);
}



 slave定期检查当前时间和上一次交互时间的差值是否大于最大超时时间:(time(NULL)-server.master->lastinteraction) > server.repl_timeout,如果超时就断开连接。

// 复制 cron 函数,每秒调用一次
void replicationCron(void) {

    // 尝试连接到主服务器,但超时
    if (server.masterhost &&
        (server.repl_state == REDIS_REPL_CONNECTING ||
         server.repl_state == REDIS_REPL_RECEIVE_PONG) &&
        (time(NULL)-server.repl_transfer_lastio) > server.repl_timeout)
    {
        redisLog(REDIS_WARNING,"Timeout connecting to the MASTER...");
        // 取消连接
        undoConnectWithMaster();
    }

    // RDB 文件的传送已超时?
    if (server.masterhost && server.repl_state == REDIS_REPL_TRANSFER &&
        (time(NULL)-server.repl_transfer_lastio) > server.repl_timeout)
    {
        redisLog(REDIS_WARNING,"Timeout receiving bulk data from MASTER... If the problem persists try to set the 'repl-timeout' parameter in redis.conf to a larger value.");
        // 停止传送,并删除临时文件
        replicationAbortSyncTransfer();
    }

    // 从服务器曾经连接上主服务器,但现在超时
    if (server.masterhost && server.repl_state == REDIS_REPL_CONNECTED &&
        (time(NULL)-server.master->lastinteraction) > server.repl_timeout)
    {
        redisLog(REDIS_WARNING,"MASTER timeout: no data nor PING received...");
        // 释放主服务器
        freeClient(server.master);
    }

    // 尝试连接主服务器
    if (server.repl_state == REDIS_REPL_CONNECT) {
        redisLog(REDIS_NOTICE,"Connecting to MASTER %s:%d",
            server.masterhost, server.masterport);
        if (connectWithMaster() == REDIS_OK) {
            redisLog(REDIS_NOTICE,"MASTER <-> SLAVE sync started");
        }
    }
}


正常关闭

 判断socket正常的关闭的途径就是通过socket的read方法来判断:

  • 读取报文数据出错
  • 读取报文长度为0,这里需要解释下:
     1. TCP recv返回0, 说明对方关闭;
     2. 注册EPOLLERR, 收到事件是关闭;
     3. recv/send 返回-1时, 如果错误不是EWOULDBLOCK或者EINTR, 也主动关闭连接。
void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
   
    // 读入内容到查询缓存
    nread = read(fd, c->querybuf+qblen, readlen);

    // 读入出错
    if (nread == -1) {
        if (errno == EAGAIN) {
            nread = 0;
        } else {
            redisLog(REDIS_VERBOSE, "Reading from client: %s",strerror(errno));
            freeClient(c);
            return;
        }
    // 遇到 EOF
    } else if (nread == 0) {
        redisLog(REDIS_VERBOSE, "Client closed connection");
        freeClient(c);
        return;
    }
}
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
1月前
|
NoSQL 算法 安全
Redlock 算法-主从redis分布式锁主节点宕机锁丢失的问题
Redlock 算法-主从redis分布式锁主节点宕机锁丢失的问题
154 0
|
3月前
|
存储 监控 NoSQL
Redis 高可用之主从模式
上一节RDB和AOF持久化机制提到了 Redis 的持久性,也就是在服务器实例宕机或故障时,拥有再恢复的能力。但是在这个服务器实例宕机恢复期间,是无法接受新的数据请求。对于整体服务而言这是无法容忍的,因此我们可以使用多个服务器实例,在一个实例宕机中断时,另外的服务器实例可以继续对外提供服务,从而不中断业务。Redis 是如何做的呢?Redis 做法是**增加冗余副本**,**将一份数据同时保存在多个实例**上。那么如何保存各个实例之间的数据一致性呢?
46 0
Redis 高可用之主从模式
|
5月前
|
存储 监控 NoSQL
redis主从模式,redis哨兵模式,redis集群模式
redis主从模式,redis哨兵模式,redis集群模式
redis主从模式,redis哨兵模式,redis集群模式
|
1月前
|
运维 负载均衡 NoSQL
【大厂面试官】知道Redis集群和Redis主从有什么区别吗
集群节点之间的故障检测和Redis主从中的哨兵检测很类似,都是通过PING消息来检测的。。。面试官抓抓脑袋,继续看你的简历…得想想考点你不懂的😰。
67 1
|
3月前
|
存储 负载均衡 NoSQL
Redis 高可用篇:你管这叫主从架构数据同步原理?
Redis 高可用篇:你管这叫主从架构数据同步原理?
241 5
|
3月前
|
缓存 NoSQL 算法
《吊打面试官》系列-Redis哨兵、持久化、主从、手撕LRU
《吊打面试官》系列-Redis哨兵、持久化、主从、手撕LRU
43 0
|
3月前
|
存储 NoSQL 数据库连接
Redis主从模式以及数据同步原理:全量数据同步、增量数据同步
Redis主从模式以及数据同步原理:全量数据同步、增量数据同步
180 0
|
3月前
|
NoSQL Ubuntu Redis
redis主从配置+集群搭建
redis主从配置+集群搭建
38 0
|
4月前
|
缓存 NoSQL 应用服务中间件
分布式缓存之Redis(持久化、主从、哨兵、分片集群)
分布式缓存之Redis(持久化、主从、哨兵、分片集群)
|
4月前
|
存储 缓存 负载均衡
REDIS主从配置
Redis的主从配置是指在Redis集群中,将一个Redis节点配置为主节点(master),其他节点配置为从节点(slave),主节点负责写操作和部分读操作,从节点负责读操作。
76 1

热门文章

最新文章