Redis设计与实现 笔记 第十七章 集群 cluster

集群

Redis 集群是 Redis 提供的分布式数据库方案,集群通过分片来进行数据共享,并提供复制和故障转移功能

17.1 节点

一个 Redis 集群通常由多个节点组成,在刚开始的时候,每个节点都处于一个只包含自己的集群中,如果需要将各个独立的节点连接起来,构成一个包含多个节点的集群.

命令如下

CLUSTER MEET <ip> <port>
if (!strcasecmp(c->argv[1]->ptr,"meet") && c->argc == 4) {
    /* CLUSTER MEET <ip> <port> */
    // 将给定地址的节点添加到当前节点所处的集群里面

    long long port;

    // 检查 port 参数的合法性
    if (getLongLongFromObject(c->argv[3], &port) != REDIS_OK) {
        addReplyErrorFormat(c,"Invalid TCP port specified: %s",
                            (char*)c->argv[3]->ptr);
        return;
    }

    // 尝试与给定地址的节点进行连接
    if (clusterStartHandshake(c->argv[2]->ptr,port) == 0 &&
        errno == EINVAL)
    {
        // 连接失败
        addReplyErrorFormat(c,"Invalid node address specified: %s:%s",
                        (char*)c->argv[2]->ptr, (char*)c->argv[3]->ptr);
    } else {
        // 连接成功
        addReply(c,shared.ok);
    }

}
17.1.1 启动节点

在数据库配置时,通过修改 cluster-enabled 配置选项来决定是否开启服务器的集群模式.

 if (!strcasecmp(argv[0],"cluster-enabled") && argc == 2) {
        if ((server.cluster_enabled = yesnotoi(argv[1])) == -1) {
            err = "argument must be 'yes' or 'no'"; goto loaderr;
        }
    }
}

节点会继续使用所有在单机模式中使用的服务组件.

1): 节点会继续使用文件事件处理器来处理命令请求和返回命令
2): 节点会继续使用时间时间处理器来执行 serverCron 函数, 而处于集群的服务器又会调用 clusterCron 函数.

 run_with_period(100) {
    if (server.cluster_enabled) clusterCron();
}

3): 节点会继续使用数据库来保存键值对数据.
4): 节点会继续使用 RDB 持久化模块和 AOF 持久化模块来进行持久化工作.
5): 节点会继续使用发布与订阅模块来执行 PUBLISH, SUBSCRIBE 等命令.
6): 节点会继续使用复制模块来进行节点的复制工作
7): 节点会继续使用 LUA 脚本环境来执行客户端输入的 LUA 脚本

Cluster 额外的数据结构使用

struct clusterNode
struct clusterLink
struct clusterState
17.1.2 集群的数据结构

用于集群当前节点属性的保存

// 节点状态
struct clusterNode {

   // 创建节点的时间
   mstime_t ctime; /* Node object creation time. */

   // 节点的名字,由 40 个十六进制字符组成
   // 例如 68eef66df23420a5862208ef5b1a7005b806f2ff
   char name[REDIS_CLUSTER_NAMELEN]; /* Node name, hex string, sha1-size */

   // 节点标识
   // 使用各种不同的标识值记录节点的角色(比如主节点或者从节点),
   // 以及节点目前所处的状态(比如在线或者下线)。
   int flags;      /* REDIS_NODE_... */

   // 节点当前的配置纪元,用于实现故障转移
   uint64_t configEpoch; /* Last configEpoch observed for this node */

   // 由这个节点负责处理的槽
   // 一共有 REDIS_CLUSTER_SLOTS / 8 个字节长
   // 每个字节的每个位记录了一个槽的保存状态
   // 位的值为 1 表示槽正由本节点处理,值为 0 则表示槽并非本节点处理
   // 比如 slots[0] 的第一个位保存了槽 0 的保存情况
   // slots[0] 的第二个位保存了槽 1 的保存情况,以此类推
   unsigned char slots[REDIS_CLUSTER_SLOTS/8]; /* slots handled by this node */

   // 该节点负责处理的槽数量
   int numslots;   /* Number of slots handled by this node */

   // 如果本节点是主节点,那么用这个属性记录从节点的数量
   int numslaves;  /* Number of slave nodes, if this is a master */

   // 指针数组,指向各个从节点
   struct clusterNode **slaves; /* pointers to slave nodes */

   // 如果这是一个从节点,那么指向主节点
   struct clusterNode *slaveof; /* pointer to the master node */

   // 最后一次发送 PING 命令的时间
   mstime_t ping_sent;      /* Unix time we sent latest ping */

   // 最后一次接收 PONG 回复的时间戳
   mstime_t pong_received;  /* Unix time we received the pong */

   // 最后一次被设置为 FAIL 状态的时间
   mstime_t fail_time;      /* Unix time when FAIL flag was set */

   // 最后一次给某个从节点投票的时间
   mstime_t voted_time;     /* Last time we voted for a slave of this master */

   // 最后一次从这个节点接收到复制偏移量的时间
   mstime_t repl_offset_time;  /* Unix time we received offset for this node */

   // 这个节点的复制偏移量
   long long repl_offset;      /* Last known repl offset for this node. */

   // 节点的 IP 地址
   char ip[REDIS_IP_STR_LEN];  /* Latest known IP address of this node */

   // 节点的端口号
   int port;                   /* Latest known port of this node */

   // 保存连接节点所需的有关信息
   clusterLink *link;          /* TCP/IP link with this node */

   // 一个链表,记录了所有其他节点对该节点的下线报告
   list *fail_reports;         /* List of nodes signaling this as failing */

};
typedef struct clusterNode clusterNode;

用于保存连接节点所需的有关信息.

/* clusterLink encapsulates everything needed to talk with a remote node. */
// clusterLink 包含了与其他节点进行通讯所需的全部信息
typedef struct clusterLink {

    // 连接的创建时间
    mstime_t ctime;             /* Link creation time */

    // TCP 套接字描述符
    int fd;                     /* TCP socket file descriptor */

    // 输出缓冲区,保存着等待发送给其他节点的消息(message)。
    sds sndbuf;                 /* Packet send buffer */

    // 输入缓冲区,保存着从其他节点接收到的消息。
    sds rcvbuf;                 /* Packet reception buffer */

    // 与这个连接相关联的节点,如果没有的话就为 NULL
    struct clusterNode *node;   /* Node related to this link if any, or NULL */

} clusterLink;

clusterState结构用于保存当前视角下,整体的集群状态

// 集群状态,每个节点都保存着一个这样的状态,记录了它们眼中的集群的样子。
// 另外,虽然这个结构主要用于记录集群的属性,但是为了节约资源,
// 有些与节点有关的属性,比如 slots_to_keys 、 failover_auth_count 
// 也被放到了这个结构里面。
typedef struct clusterState {

    // 指向当前节点的指针
    clusterNode *myself;  /* This node */

    // 集群当前的配置纪元,用于实现故障转移
    uint64_t currentEpoch;

    // 集群当前的状态:是在线还是下线
    int state;            /* REDIS_CLUSTER_OK, REDIS_CLUSTER_FAIL, ... */

    // 集群中至少处理着一个槽的节点的数量。
    int size;             /* Num of master nodes with at least one slot */

    // 集群节点名单(包括 myself 节点)
    // 字典的键为节点的名字,字典的值为 clusterNode 结构
    dict *nodes;          /* Hash table of name -> clusterNode structures */

    // 节点黑名单,用于 CLUSTER FORGET 命令
    // 防止被 FORGET 的命令重新被添加到集群里面
    // (不过现在似乎没有在使用的样子,已废弃?还是尚未实现?)
    dict *nodes_black_list; /* Nodes we don't re-add for a few seconds. */

    // 记录要从当前节点迁移到目标节点的槽,以及迁移的目标节点
    // migrating_slots_to[i] = NULL 表示槽 i 未被迁移
    // migrating_slots_to[i] = clusterNode_A 表示槽 i 要从本节点迁移至节点 A
    clusterNode *migrating_slots_to[REDIS_CLUSTER_SLOTS];

    // 记录要从源节点迁移到本节点的槽,以及进行迁移的源节点
    // importing_slots_from[i] = NULL 表示槽 i 未进行导入
    // importing_slots_from[i] = clusterNode_A 表示正从节点 A 中导入槽 i
    clusterNode *importing_slots_from[REDIS_CLUSTER_SLOTS];

    // 负责处理各个槽的节点
    // 例如 slots[i] = clusterNode_A 表
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值