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;
}

  

 

 

posted @ 2020-12-01 14:57  雪域蓝心  阅读(128)  评论(0编辑  收藏  举报