【PG高可用】Repmgr源码分析之主库监控

news2024/11/24 5:45:28

repmgr需要在集群中每个节点上以扩展的形式安装插件,运行在每个节点上的repmgrd可以监控复制以及执行故障转移或切换等操作增强 PostgreSQL 的内置复制功能。

如何可靠快速的监控主节点故障一般是所有数据库高可用程序中都会有的环节,本篇内容主要介绍不同角色的节点如何监控主节点是否存活。

函数入口

通过命令 repmgrd -f repmgr.conf 启动repmgrd后台监控程序时,进入程序的主函数 main(repmgrd.c文件)函数中,

在main 函数主要是处理命令行参数,获取本地节点的信息

	/* Retrieve record for this node from the local database */
	record_status = get_node_record(local_conn, config_file_options.node_id, &local_node_info);

通过SQL查询repmgr数据库中 nodes表 获取本地节点信息 ,查询SQL如下

	appendPQExpBuffer(&query,
					  "SELECT " REPMGR_NODES_COLUMNS
					  "  FROM repmgr.nodes n "
					  " WHERE n.node_id = %i",
					  node_id);

通过函数 start_monitoring(void) 启动对本地节点的监控,

首先会判断本地节点的角色,根据不同的节点角色进入到不同的监控函数中:

primary节点 对应 monitor_streaming_primary()

standby节点 对应 monitor_streaming_standby()

witness节点 对应 monitor_streaming_witness()

static void
start_monitoring(void)
{
	log_notice(_("starting monitoring of node \"%s\" (ID: %i)"),
			   local_node_info.node_name,
			   local_node_info.node_id);

	log_info(_("\"connection_check_type\" set to \"%s\""), print_connection_check_type(config_file_options.connection_check_type));
    // 进入一个无限循环
	while (true)
	{
	    // 根据节点的不同角色 ,进入到不同的监控处理分支
		switch (local_node_info.type)
		{
		    // 主节点
			case PRIMARY:
				monitor_streaming_primary();
				break;
			// 备节点
			case STANDBY:
				monitor_streaming_standby();
				break;
			// 见证节点
			case WITNESS:
				monitor_streaming_witness();
				break;
			case UNKNOWN:
				/* should never happen */
				break;
		}
	}
}

monitor_streaming_primary

如果是节点角色是PRIMARY,会进入到 monitor_streaming_primary 分支进行监控本地的主节点,以下是主要流程

1 重置节点的投票信息,设置节点当前不参与投票 VS_NO_VOTE

执行函数 reset_node_voting_status();

reset_node_voting_status();

该函数会调用执行数据库的函数 repmgr.reset_voting_status()

SELECT repmgr.reset_voting_status()

在数据库中查看 函数 reset_voting_status 定义,它告诉PostgreSQL查找‘libdir/repmgr动态链接库(DLL 或 so 文件),并在其中查找名为repmgr_reset_voting_status` 的函数。

repmgr=#\sf reset_voting_status
CREATE OR REPLACE FUNCTION repmgr.reset_voting_status()
 RETURNS void
 LANGUAGE c
 STRICT
AS '$libdir/repmgr', $function$repmgr_reset_voting_status$function$


函数解释

  • CREATE OR REPLACE FUNCTION: 如果函数已存在,则替换它;如果不存在,则创建它。

  • repmgr.reset_voting_status(): 函数名,表明这个函数属于 repmgr schema,并且函数名为 reset_voting_status

  • RETURNS void: 函数不返回任何值。

  • LANGUAGE c: 函数是用 C 语言编写的。

  • STRICT: 如果任何输入参数为 NULL,则函数不会执行并立即返回 NULL。

  • **AS 'libdir/repmgr′,functionrepmgrr​esetv​otings​tatusfunction∗∗:指定了函数的实现。这里,它告诉PostgreSQL查找‘libdir/repmgr动态链接库(DLL 或 so 文件),并在其中查找名为repmgr_reset_voting_status` 的函数。

查看 repmgr 源码中的函数 repmgr_reset_voting_status

Datum
repmgr_reset_voting_status(PG_FUNCTION_ARGS)
{   // 如果 shared_state 不为空 ,说明不是第一次启动?
	if (!shared_state)
		PG_RETURN_NULL();
    // 获取共享的锁
	LWLockAcquire(shared_state->lock, LW_SHARED);

	/* only do something if local_node_id is initialised */
	// 如果local_node_id 是初始化的值 ,则做一些操作
	if (shared_state->local_node_id != UNKNOWN_NODE_ID)
	{   // 释放共享锁
		LWLockRelease(shared_state->lock);
		// 获取排他锁
		LWLockAcquire(shared_state->lock, LW_EXCLUSIVE);
        // 将该节点的投票状态设置为 VS_NO_VOTE 不参与投票
		shared_state->voting_status = VS_NO_VOTE;
		// 将后端节点的node id 设置为UNKNOWN_NODE_ID
		shared_state->candidate_node_id = UNKNOWN_NODE_ID;
		// follow_new_primary 设置为 不
		shared_state->follow_new_primary = false;
	}
    // 释放锁
	LWLockRelease(shared_state->lock);

	PG_RETURN_VOID();
}

指针类型,共享内存的结构体

static repmgrdSharedState *shared_state = NULL;

shared_state 结构体

typedef struct repmgrdSharedState
{
	LWLockId	lock;			/* protects search/modification */
	TimestampTz last_updated;
	int			local_node_id;
	int			repmgrd_pid;
	char		repmgrd_pidfile[MAXPGPATH];
	bool		repmgrd_paused;
	/* streaming failover */
	int			upstream_node_id;
	TimestampTz upstream_last_seen;
	NodeVotingStatus voting_status;
	int			current_electoral_term;
	int			candidate_node_id;
	bool		follow_new_primary;
} repmgrdSharedState;

投票状态是一个枚举类型

typedef enum
{
	VS_UNKNOWN = -1,
	VS_NO_VOTE,
	VS_VOTE_REQUEST_RECEIVED,
	VS_VOTE_INITIATED
} NodeVotingStatus;

2 重置主节点的上游节点id 为 ​NO_UPSTREAM_NODE,因为主节点 不需要follow 任何其他节点

repmgrd_set_upstream_node_id(local_conn, NO_UPSTREAM_NODE);

3 根据 startup_event_logged 的值,发送一个 repmgrd_start(如果repmgrd未启动) 或 repmgrd_reload (如果repmgred已启动) 的事件通知

4 获取下游子节点的信息

bool success = get_child_nodes(local_conn, config_file_options.node_id, &db_child_node_records);

查询下游节点的SQL语句

appendPQExpBuffer(&query,
              "    SELECT n.node_id, n.type, n.upstream_node_id, n.node_name, n.conninfo, n.repluser, "
              "           n.slot_name, n.location, n.priority, n.active, n.config_file, "
              "           '' AS upstream_node_name, "
              "           CASE WHEN sr.application_name IS NULL THEN FALSE ELSE TRUE END AS attached "
              "      FROM repmgr.nodes n "
              " LEFT JOIN pg_catalog.pg_stat_replication sr "
              "        ON sr.application_name = n.node_name "
              "     WHERE n.upstream_node_id = %i ",
              node_id);

5 进入到while (true) 循环

51  (void) connection_ping(local_conn);  防止数据库连接过期失效 发出SELECT TRUE

5.2 check_connection(&local_node_info, &local_conn); 会对本地节点进行两次检查

5.3 如果节点状态是连接不正常的(PQstatus(local_conn) != CONNECTION_OK),而且原来的状态也被标记为启动的(NODE_STATUS_UP),会初始化一个本地节点不可用开始时间的变量(local_node_unreachable_start),并且把该变量设置为当前时间,然后发送一个 repmgrd_local_disconnect 事件通知,并将节点状态设置为 NODE_STATUS_UNKNOWN 。

5.4 为了防止网络抖动,会进行连接重试,最多重试多少次,每次重试间隔多长时间可以通过配置文件配置

try_reconnect(&local_conn, &local_node_info);

5.5 如果在上面重试过程中,节点状态又变为可用了(NODE_STATUS_UP) ,则 

  • 计算当前节点不可用时间

  • 当前节点设置为 UNKNOWN_NODE_ID

  • 准备事件通知

  • 记录日志

  • 发送事件通知 repmgrd_local_reconnect

  • 重新设置node_id  和 pid

  • 如果不是在主节点,则函数返回,并进入到loop

5.6 如果节点状态还是不可用的,则将节点监控状态设置为降级监控模式 MS_DEGRADED,并初始化降级监控的开始时间为当前时间。

注意

1 主节点不参与投票?

2 主节点上的后台进程不会进行切换操作,可能考虑到 ,主节点服务器宕机,提升命令都需要在本地执行

3 主节点上的repmgrd 会获取下游节点信息,并检查下游节点的状态

4 主节点loop 部分逻辑

  • 检查是否还是主节点 ,角色改变则函数返回return,重新判断节点

  • 每隔log_status_interval秒记录日志

  • reload repmgr配置

  • 监控休眠monitor_interval_secs秒

monitor_streaming_standby

忽略初始化部分 ,进入到 while(true)环节

1 执行检查上游节点连通性的函数 check_upstream_connection ,如果连接正常,更新内存中结构体 shared_state 中最新一次检查的时间

	shared_state->upstream_last_seen = GetCurrentTimestamp();
	shared_state->upstream_node_id = upstream_node_id;

2 如果上游节点连接不正常 ,会尝试 reconnect_attempts 次 重新连接到上游节点

primary_node_id = try_primary_reconnect(&upstream_conn, local_conn, &upstream_node_info);

3 如果主节点的node_id 为 ELECTION_RERUN_NOTIFICATION ,则进行 主库切换 do_primary_failover ,函数返回

4如果主节点的node_id 不是 UNKNOWN_NODE_ID 且 不是 ELECTION_RERUN_NOTIFICATIO,则进行跟随新的主库 ,函数返回

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1983055.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

掌握 Linux 信号机制的技巧与策略

目录 一.信号的产生1.信号的产生(预备)2.异常(1).硬件异常(2).core dump(3).软件条件产生信号 二.信号的保存1.信号的发送2.block.pending.handler(保存)(1).sigset_t类型 三.信号的捕捉处理1.什么时候捕捉2.三顾进程地址空间3.如…

深圳水务展|2025深圳国际水务科技博览会

2025深圳国际水务科技博览会 展会主题: 新质生产力赋能水务产业高质量发展 展会时间:2025年7月24-26日 展会地点:深圳会展中心(福田) 主办单位: 广东省水利学会 深圳市水务学会 协办单位: 中国水利…

发论文idea来了!小样本学习+目标检测,12个创新点汇总

在一些特定应用领域,获取大规模且高质量标注的数据十分困难,比如医学图像分析。为了解决这个问题,研究者们提出了小样本目标检测。 小样本目标检测是一种结合了小样本学习和目标检测两者优势的技术,能够在有限的训练数据下&#…

elementPuls_Treeg更改颜色

elementPuls_Treeg更改颜色 .el-tree {background: transparent;color: #fff;}:deep(.el-tree-node__content:hover) {background: rgba(2, 167, 240, 0.5);}//选中的背景色:deep(.el-tree--highlight-current.el-tree-node.is-current> .el-tree-node__content) {backgrou…

上班族必备!这款免费录屏工具让你工作效率翻倍

www.bandicam.com/downloads/现在还依稀记得疫情爆发的那一年在学校上网课的场景,在家里上着网课担心错过老师讲的重点,就特地找了录屏的工具来使用,帮我录制重点片段,今天就针对录屏的工具整理了四款免费的录屏软件,有…

Linux系统驱动(五)

文章目录 一、实现机制二、字符设备驱动分布实现流程三、添加自己的系统调用函数1. 找到系统调用文件2. 找到 一、实现机制 应用层 vfs层 驱动层 字符设备按照字节流顺序访问,但是实际它提供了无序访问的功能 vi -t sys_open 内核中通过inode号可以唯一的找到一…

C语言典型例题27

《C程序设计教程(第四版)——谭浩强》 习题2.4 用下面的scanf函数输入数据 使a3,b7,x8.5,y71.8,c1A,c2a。问在键盘上怎么输入 代码 //《C程序设计教程(第四版)——谭浩强》 //习题2.4 用下面的scanf函数输入数据,使…

CentOS安装sentry

Sentry介绍 Sentry 是一套开源的实时的异常收集、追踪、监控系统。这套解决方案由对应各种语言的 SDK 和一套庞大的数据后台服务组成,通过 Sentry SDK 的配置,还可以上报错误关联的版本信息、发布环境。同时 Sentry SDK 会自动捕捉异常发生前的相关操作&…

电线电缆测厚双测径仪联控测厚系统

关键字:线缆测厚系统,绝缘层测厚设备,电线皮套测厚,电缆绝缘层测厚, 产品简介: 双测径仪联控测厚系统的工作原理基于光电测量技术。一台测径仪测量电缆的成品直径,另一台测径仪测量线芯的直径。通过这些测量数据,系统计算出绝缘层或护套层的厚…

IT课程学习搭子

各种IT课程齐全可学,价格你说了算,相比于培训班有以下优势: 1、避免被割韭菜,避免踩坑,避免交智商税,最低的成本学最有价值的课,同时又能达到比培训班更好的效果 2、收徒,带你学习 本…

第十五节、三段攻击动画的实现

一、创建攻击动画 新建图层 新建状态 放入攻击动画 二、攻击实现 三段式攻击 1、按下触发三段式 2、按键触发第一下攻击 设立两个参数 一个计数器 计数器并未使用 三、代码实现 1、注册攻击事件 2、设置动画参数关联

中国制造2025,会抛弃精益生产吗?

时至今日,“精益生产”模式依旧大行其道,它始终支持着中国制造业以最低的成本做出优质产品。我们认为,纵然是中国制造2025成为现实,精益生产模式也仍然是整个制造业的精髓之一。 首先,精益生产模式最重要的一根脊梁就是…

【密码学】密码协议的分类:①密钥建立协议

密码协议的分类有很多种方式,这里我采取的是基于协议实现的目的来分类。可以将密码协议分成三类:认证协议、密钥建立协议、认证密钥建立协议。这些协议在密码学和网络安全中扮演着至关重要的角色,下面我来对密钥建立协议详细介绍 密钥建立协议…

Rsync未授权访问漏洞 *

Rsync是Linux/Unix下的一个远程数据同步工具,可通过LAN/WAN快速同步多台主机间的文件和目录,默认运行在873端口。由于配置不当,导致任何人可未授权访问rsync,上传本地文件,下载服务器文件。Rsync 默认允许匿名访问&…

mysql数据库数据类型和约束

mysql数据库:数据类型和约束 常见的数据类型和约束 数据类型 数值类型:INT、BIGINT、FLOAT、DOUBLE,DECIMAL等。字符串类型:CHAR、VARCHAR、TEXT等。日期和时间类型:DATE、DATETIME、TIMESTAMP等。二进制类型:BLOB、LO…

《机器学习by周志华》学习笔记-决策树-02

1、剪枝处理(Pruning) 1.1、背景概念 上文「决策树01」的学习中,我们了解了著名的3种决策树算法ID3、C4.5、CART。这3种决策树算法最根本的核心就是根据特征选择离散属性作为节点来搭建树结构,运用搭好的结构进行推理。 剪枝(pruning)则就是将搭好的决策树去掉一些「非叶节…

[RTOS 学习记录] 预备知识:C语言结构体

这篇文章是我阅读《嵌入式实时操作系统μCOS-II原理及应用》后的读书笔记,记录目的是为了个人后续回顾复习使用。 文章目录 结构体结构体基础声明和定义结构体类型声明和定义结构体变量初始化结构体变量初始化各个成员使用列表符号初始化 使用结构体变量综上 结构体…

C语言程序设计-[5] 输入输出语句

C语言提供了一些输入输出的库函数,使用库函数,必须将相应的头文件“stdio.h”包含进来。 输入输出库函数可分为三类:字符输入输出函数、字符串输入输出函数和格式化输入输出函数。前两类功能单一,使用起来相对简单,以…

消息队列:Kafka吞吐量为什么比RocketMQ大

根据资料显示RocketMQ每秒能处理10W量级数据,而Kafka能处理17W量级数据。 这两者差别主要再使用的零拷贝技术不一样。 再什么情况下零拷贝技术诞生了 为了防止消息队列中的消息因为各种意外情况丢失,要对消息进行持久化处理,将其存储在磁盘…

Dubbo未授权访问漏洞

Dubbo是阿里巴巴公司开源的一个高性能优秀的 服务框架,使得应用可通过高性能的 RPC 实现服务的输 出和输入功能,可以和 Spring框架无缝集成。dubbo 因配置不当导致未授权访问漏洞。 》》》漏洞复现《《《 步骤一:使用以下语句在Fofa上进行资…