前言
hiredis不支持哨兵模式,但是我们需要知道主节点的ip端口,不然无法正常写入,如果对redis很清晰的话,可以跳过下面的概念部分,本文基于原版hiredis做的。
集群模式
需要至少三个主节点才能开启,每个主节点分担n分支一的数据,也就是只要有主节点挂了,数据就丢失了(三个主节点就是每个节点承担三分之一的数据)
Redis集群_一只猪的思考的博客-CSDN博客_redis集群
主从模式
主节点能写能读,从节点只能读,缺点就是主节点挂了,尽管几个从节点重新选择了主节点,但是用户不知道最终选举的是哪个节点为主节点(解决办法就是哨兵模式)
redis主从设置_lt9700的博客-CSDN博客_redis主从
哨兵模式
哨兵的出现就是为了监视redis是否正常运转,一个哨兵监测一个redis节点,哨兵节点掌握了哪个是主节点的消息,用户直接跟哨兵通信,就知道哪个是主节点了。
每个哨兵都是独立的进程,有自己的监听端口,并且可以设置哨兵的独立密码(非redis登陆密码)。
但是哨兵如果挂了怎么办,所以用户通常一次会保存多个哨兵节点的ip以及端口,一个哨兵挂了,就问另外的哨兵,要是都挂了就没办法了。
可能有同学就要问了,那我直接保存所有redis节点不就行了,那还要哨兵干嘛,本文后面的代码就是基于这个思路写的。最大的缺点就是需要一个个尝试,我问哨兵一次我就知道哪个是主节点(不过从节点也会存储主节点的ip信息,理论上也能读取到主节点的信息),而且哨兵节点挂的几率比redis小的多,因为不承担业务。
Redis的哨兵模式_p&f°的博客-CSDN博客_redis哨兵模式
关于集群
hiredis并没有提供集群的库,不过有人基于hiredis做了集群的库
(下面的网址基于hiredis 1.0版本制作)
(下面的网址集成了旧版本hiredis,下面这个hiredis-vip库不支持auth认证,也没有人维护了,不过有人提了合并代码请求关于认证的,可以看看)
https://github.com/vipshop/hiredis-vip
集群跟主从没有必然联系,没开集群,也可以启用主从
hiredis-vip这个库可能会报错下面这个,说集群没开启
ERR This instance has cluster support disabled
应该是尝试了集群的命令,发现集群没开,redis的修改配置文件就可以开启集群了,如
思路
先从配置文件读取当前所有的节点信息,配置文件如下,使用逗号分割ip地址
[redis]
redis-server = 192.168.174.190:6379,192.168.174.191:6380,192.168.174.192:6381
再挨个连过去,我们知道,使用role命令能知道自己是不是主节点,我们基于这个命令判断主节点(role命令返回的数组类型) (感兴趣的读者可以尝试另外一个思路,就是如果数组第二项是ip而非数字,那么第二项的ip必为主节点,反之当前节点为主节点)
# redis-cli -h 192.168.174.190 -p 6379
192.168.174.190:6379> auth 123456
OK
192.168.174.190:6379> role
1) "master"
2) (integer) 2868563
3) 1) 1) "192.168.174.191"
2) "6380"
3) "2868563"
2) 1) "192.168.174.192"
2) "6381"
3) "2868563"
192.168.174.190:6379> exit
# redis-cli -h 192.168.174.191 -p 6380
192.168.174.191:6380> auth 123456
OK
192.168.174.191:6380> role
1) "slave"
2) "192.168.174.190"
3) (integer) 6379
4) "connected"
5) (integer) 2868619
代码
解析配置文件的头文件 (ini.h)
代码细节
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<hiredis/hiredis.h>
#include"ini.h"
typedef struct
{
char* redis_server;
} configuration;
static int handler(void* config, const char* section, const char* name,
const char* value)
{
configuration* pconfig = (configuration*)config;
#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
if(MATCH("redis", "redis-server")){
pconfig->redis_server = strdup(value);
} else {
return 1;
}
return 0;
}
int main()
{
int res = 1;
// 解析配置文件
configuration config;
char* path = "/home/redis-server.ini";
if (ini_parse(path, handler, &config) < 0) {
printf("Can't load %s\n", path);
return 1;
}
// 寻找节点中的主节点
char ip[32] = {0};
int port = -1;
char* redis_str = NULL;
// 以逗号分割多个节点的ip端口,每次取出一个进行尝试连接
// config.redis_server = "192.168.174.190:6379,192.168.174.191:6380,192.168.174.192:6381"
redis_str = strtok(config.redis_server, ",");
while (redis_str != NULL)
{
// 只支持ip,域名不确定能不能做
sscanf(redis_str, "%[0-9.]:%d", ip, &port);
if (check_master(ip, port) == 0){
res = 0;
break;
}
redis_str = strtok(NULL, ",");
}
// 打印结果
if (res == 0) {
printf("Find the master. ip %s, port %d\n", ip, port);
} else {
printf("Not find the master\n");
}
if (config.redis_server)
free(config.redis_server);
return;
}
int check_master(char *host, int port)
{
// 连接redis
redisContext* context = redisConnect(host, port);
if (context == NULL || context->err) {
printf("Failed to initializing redis\n");
return 1;
}
// 有密码认证要先密码认证
redisCommand(context, "auth Admin_123");
// 向redis的服务端发送 role
redisReply* reply = redisCommand(context, "role");
if (reply == NULL || context->err || reply->type != REDIS_REPLY_ARRAY) {
const char *cause = context->err ? context->errstr : "(none)";
printf("Failed to get redis status. %s\n", cause);
if (reply)
freeReplyObject(reply);
redisFree(context);
return 1;
}
// 判断返回里是否有 master,如果有则为主节点
if (strstr(reply->element[0]->str, "master")){
freeReplyObject(reply);
redisFree(context);
return 0;
}
freeReplyObject(reply);
redisFree(context);
return 1;
}
编译命令
gcc -o test.out test.c ini.c -lhiredis