redis sentinel 集群
参照课程:https://www.bilibili.com/video/BV1VJ411B7Kr?p=4
redis sentinel 集群【哨兵 主从复制集群】
* 哨兵要做的事情:
. 将宕机的master下线
. 找slave作为master
. 通知所有的slave来连接新的master
.启动新的master 与 slave
. 全量复制*N + 部分复制*N
***********************************
哨兵来确认master宕机
哨兵选举master
主恢复如何处理,哨兵把主变为从
总结哨兵作用:
监控:
不断的检查master 和 slave是否正常运行。
master 存活检测、master与slave运行情况检测。
通知:
当被监控的服务器出现问题时,向其他(哨兵,客户端)发送通知。
自动故障转移:
断开master 与 slave 连接,选取一个slave作为master,将其他slave连接到新的master,并告知客户端新的服务器地址。
【注意】
sentinel 之间是通过发布订阅的方式来同步信息。
哨兵也是一台 redis 服务器,只是不提供数据服务
通常哨兵配置数量为单数
1. 主观下线(Subjectively Down, 简称 SDOWN)指的是单个 Sentinel 实例对服务器做出的下线判断。
2. 客观下线(Objectively Down, 简称 ODOWN)指的是多个 Sentinel 实例在对同一个服务器做出 SDOWN 判断, 并且通过 SENTINEL is-master-down-by-addr 命令互相交流之后, 得出的服务器下线判断。
选举:
sentinel 之间选举出一个负责的sentinel x, 然后 负责人 x 从slave 根据网络响应,数据最新的slave作为master.
*搭建环境 --> 3个哨兵 1 主 4从
查看sentinel.conf内容
cat sentinel.conf | grep -v "#" | grep -v "^$"

修改配置:
* 修改sentinel.conf
port 7000
daemonize yes
pidfile /var/run/redis-sentinel7000.pid
logfile "/mnt/hgfs/linux_share/redis_sentinel/redis_s7000/redis.log"
dir /mnt/hgfs/linux_share/redis_sentinel/redis_s7000
sentinel monitor mymaster 127.0.0.1 7003 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes
将配置文件修改到 7001 7002下:
sed 's/7000/7001/g' redis_s7000/sentinel.conf > redis_s7001/sentinel.conf
sed 's/7000/7002/g' redis_s7000/sentinel.conf > redis_s7002/sentinel.conf
* 修改redis.conf

sed 's/7003/7004/g' redis_m7003/redis.conf > redis_sl7004/redis.conf
sed 's\redis7004\redis_sl7004\g' redis_sl7004/redis.conf > redis_sl7004/redis.conf1 && mv redis_sl7004/redis.conf1 redis_sl7004/redis.conf
同时从机7004追加以下选项:【5.0版本使用REPLICAOF代替了之前版本的SLAVEOF,如果使用5.0及之后版本,则建议新命令REPLICAOF】
slaveof 192.168.153.133 7003

复制从机配置文件:
sed 's\7004\7005\g' redis_sl7004/redis.conf > redis_sl7005/redis.conf
sed 's\7004\7006\g' redis_sl7004/redis.conf > redis_sl7006/redis.conf
sed 's\7004\7007\g' redis_sl7004/redis.conf > redis_sl7007/redis.conf
启动主机从机,建立主从模式。
./redis_exe/redis-server ./redis_m7003/redis.conf
./redis_exe/redis-server ./redis_sl7004/redis.conf
./redis_exe/redis-server ./redis_sl7005/redis.conf
./redis_exe/redis-server ./redis_sl7006/redis.conf
./redis_exe/redis-server ./redis_sl7007/redis.conf
查看信息:
./redis_exe/redis-cli -h 127.0.0.1 -p 7003
info Replication

** 启动哨兵
./redis_exe/redis-sentinel ./redis_s7000/sentinel.conf
./redis_exe/redis-sentinel ./redis_s7001/sentinel.conf
./redis_exe/redis-sentinel ./redis_s7002/sentinel.conf
** 查看搭建情况
./redis_exe/redis-cli -h 127.0.0.1 -p 7000
info sentinel

查看端口:说明sentinel 之间的连接为短连接

测试集群:

查看主机信息:
./redis_exe/redis-cli -h 127.0.0.1 -p 7005
info Replication

7006 作为主机,从机无法写入。

【C++ api】
使用 "sentinel slaves mastername" 命令查找从节点。
#if REDIS_WAIT_TIME_OUT
int Command_Connect(redisContext *&con, char ip[][32], int port[], int nHandleIndex)
#else
int Command_Connect(redisContext *&con, char ip[][32], int port[], int /*nHandleIndex*/)
#endif
{
// my_con = redisConnect(ip, port);
struct timeval tv;
tv.tv_sec = 2;
tv.tv_usec = 0;
char msg[1024];
redisContext* my_con = NULL;
redisContext* real_con = NULL;
int nIPIndex = 0;
#if REDIS_WAIT_TIME_OUT
int nOutTime = 15;
// 取得股票代码的句柄
if (CONNECT_GETCODE_INDEX == nHandleIndex)
{
nOutTime = g_nPushOutTime;
}
// 取得行情数据的句柄
else if (CONNECT_PRICE_INDEX == nHandleIndex)
{
nOutTime = g_nGetPriceOutTime;
}
else
{
}
#endif
do
{
Sleep(500);
// 0-集群模式
if (0 == g_nRedisMode)
{
if (my_con != NULL)
{
redisFree(my_con);
my_con = NULL;
}
// 连接sentinel 服务器; 以带有超时的方式链接Redis服务器,同时获取与Redis连接的上下文对象。该对象将用于其后所有与Redis操作的函数。
my_con = redisConnectWithTimeout(ip[nIPIndex], port[nIPIndex], tv);
// printf("con = %lld, con->obuf---%lld....con->err---%lld\n", my_con, my_con->obuf, my_con->err);
// while ((my_con->err) || (my_con->obuf == NULL))
if (NULL == my_con)
{
memset(msg, 0x00, sizeof(msg)); sprintf_s(msg, 1024, "--redis数据库连接失败 IP:%s 端口 %d", ip[nIPIndex], port[nIPIndex]);
print_write_log(msg, m_module_name, __LINE__, m_error_log);
nIPIndex = (nIPIndex + 1) % g_nRedisCountMax;
continue;
}
if (my_con->err)
{
memset(msg, 0x00, sizeof(msg)); sprintf_s(msg, 1024, "--redis数据库连接失败 IP:%s 端口 %d", ip[nIPIndex], port[nIPIndex]);
print_write_log(msg, m_module_name, __LINE__, m_error_log);
nIPIndex = (nIPIndex + 1) % g_nRedisCountMax;
continue;
}
// 打印日志
memset(msg, 0x00, sizeof(msg)); sprintf_s(msg, 1024, "sentinel连接成功 IP:%s 端口 %d", ip[nIPIndex], port[nIPIndex]);
print_write_log(msg, m_module_name, __LINE__, m_error_log);
#if 0
// 连接备机redis
redisReply *reply = (redisReply *)redisCommand(my_con, "sentinel get-master-addr-by-name %s", g_chsentinel_master_name);
if ((reply == NULL) || (reply->elements < 2))
{
memset(msg, 0x00, sizeof(msg)); sprintf_s(msg, 1024, "--sentinel get-master-addr-by-name %s 失败, IP:%s 端口 %d", g_chsentinel_master_name, ip[nIPIndex], port[nIPIndex]);
print_write_log(msg, m_module_name, __LINE__, m_error_log);
continue;
}
// 实际redis连接
real_con = redisConnectWithTimeout(reply->element[0]->str, atoi(reply->element[1]->str), tv);
if (real_con == NULL)
{
continue;
}
if (real_con->err)
{
redisFree(real_con);
memset(msg, 0x00, sizeof(msg)); sprintf_s(msg, 1024, "--redis数据库连接失败 IP:%s 端口 %s", reply->element[0]->str, reply->element[1]->str);
print_write_log(msg, m_module_name, __LINE__, m_error_log);
continue;
}
memset(msg, 0x00, sizeof(msg)); sprintf_s(msg, 1024, "++redis数据库连接成功 IP:%s 端口 %s", reply->element[0]->str, reply->element[1]->str);
print_write_log(msg, m_module_name, __LINE__, m_error_log);
redisFree(my_con); // 释放sentinel连接
my_con = NULL;
#else
const int array_len = 128;
char all_ip[array_len][32];
int all_port[128];
int index = 0;
// 连接主节点
redisReply *reply = (redisReply *)redisCommand(my_con, "sentinel masters");
if (reply == NULL)
{
memset(msg, 0x00, sizeof(msg)); sprintf_s(msg, 1024, "--sentinel masters 失败, IP:%s 端口 %d", ip[nIPIndex], port[nIPIndex]);
print_write_log(msg, m_module_name, __LINE__, m_error_log);
}
else
{
if (reply->elements >= 1)
{
for (int i = 0; i < reply->elements; i++)
{
if (reply->element[i]->elements != 40)
{
continue;
}
//printf("master is : \t %s %s \n", reply->element[i]->element[3]->str, reply->element[i]->element[5]->str);
memset(msg, 0x00, sizeof(msg)); sprintf_s(msg, 1024, "master is : \t %s %s", reply->element[i]->element[3]->str, reply->element[i]->element[5]->str);
print_write_log(msg, m_module_name, __LINE__, m_error_log);
strcpy_s(all_ip[index], 32, reply->element[i]->element[3]->str);
all_port[index] = atoi(reply->element[i]->element[5]->str);
++index;
}
}
freeReplyObject(reply);
}
// 连接从节点
reply = (redisReply *)redisCommand(my_con, "sentinel slaves %s", g_chsentinel_master_name);
if (reply == NULL)
{
memset(msg, 0x00, sizeof(msg)); sprintf_s(msg, 1024, "--sentinel slaves %s 失败, IP:%s 端口 %d", g_chsentinel_master_name, ip[nIPIndex], port[nIPIndex]);
print_write_log(msg, m_module_name, __LINE__, m_error_log);
}
else
{
if (reply->elements >= 1)
{
for (int i = 0; i < reply->elements; i++)
{
if (reply->element[i]->elements != 40)
{
continue;
}
//printf("slaves is : \t %s %s \n", reply->element[i]->element[3]->str, reply->element[i]->element[5]->str);
memset(msg, 0x00, sizeof(msg)); sprintf_s(msg, 1024, "slaves is : \t %s %s", reply->element[i]->element[3]->str, reply->element[i]->element[5]->str);;
print_write_log(msg, m_module_name, __LINE__, m_error_log);
strcpy_s(all_ip[index], 32, reply->element[i]->element[3]->str);
all_port[index] = atoi(reply->element[i]->element[5]->str);
++index;
}
}
freeReplyObject(reply);
}
// 如果主从节点都没有取到,则continue
int real_index = GetTickCount() % index;
// 实际redis连接
real_con = redisConnectWithTimeout(all_ip[real_index], all_port[real_index], tv);
if (real_con == NULL)
{
continue;
}
if (real_con->err)
{
redisFree(real_con);
memset(msg, 0x00, sizeof(msg)); sprintf_s(msg, 1024, "--redis数据库连接失败 IP:%s 端口 %d", all_ip[real_index], all_port[real_index]);
print_write_log(msg, m_module_name, __LINE__, m_error_log);
continue;
}
memset(msg, 0x00, sizeof(msg)); sprintf_s(msg, 1024, "++redis数据库连接成功 IP:%s 端口 %d", all_ip[real_index], all_port[real_index]);
print_write_log(msg, m_module_name, __LINE__, m_error_log);
redisFree(my_con); // 释放sentinel连接
my_con = NULL;
#endif
}
// 1-单机模式
else
{
// 实际redis连接
real_con = redisConnectWithTimeout(ip[0], port[0], tv);
if (real_con->err)
{
redisFree(real_con);
memset(msg, 0x00, sizeof(msg)); sprintf_s(msg, 1024, "--redis数据库连接失败 IP:%s 端口 %d", ip[0], port[0]);
print_write_log(msg, m_module_name, __LINE__, m_error_log);
continue;
}
memset(msg, 0x00, sizeof(msg)); sprintf_s(msg, 1024, "++redis连接成功 IP:%s 端口 %d", ip[0], port[0]);
print_write_log(msg, m_module_name, __LINE__, m_error_log);
}
con = real_con;
char strcmd[256];
sprintf_s(strcmd, sizeof(strcmd), "auth %s", g_chRedisPassWord);
redisReply *reply = (redisReply*)redisCommand(con, strcmd);
// 操作失败
if ((NULL == reply) || (reply->type == REDIS_REPLY_ERROR))
{
if (NULL != reply)
{
memset(msg, 0x00, sizeof(msg)); sprintf_s(msg, 1024, "redis密码错误:%s ", reply->str);
print_write_log(msg, m_module_name, __LINE__, m_error_log);
freeReplyObject(reply);
}
}
else
{
freeReplyObject(reply);
}
#if REDIS_WAIT_TIME_OUT
// 设定超时时间
struct timeval tv;
tv.tv_sec = nOutTime * 1000; // tv_sec 的单位ms
tv.tv_usec = 0;
redisSetTimeout(con, tv);
#endif
#if REDIS_PING
// 取得实际redis 的 ip/端口
if (CONNECT_GETCODE_INDEX == nHandleIndex)
{
strcpy_s(g_chPingRedisIp[0], 32, ip[0]); //推送股票的redis的IP
g_nPingRedisPort[0] = port[0]; // 推送股票的redis的端口
}
#endif
break;
} while (true);
return 0;
}

浙公网安备 33010602011771号