PostgreSQL数据库PMsignal——后端进程\Postmaster信号通信

2024-03-07 08:10

本文主要是介绍PostgreSQL数据库PMsignal——后端进程\Postmaster信号通信,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

后端进程向Postermaster发信号

src/backend/storage/ipc/pmsignal.c
在这里插入图片描述
Postmaster拥有BackendList后端列表,用于存放标识postgres子进程的Backend。AssignPostmasterChildSlot函数为新的postmaster子进程选择未使用的slot,并将其状态设置为ASSIGNED,返回slot number。如果PMSignalState->next_child_flag为0,进入for循环从PMSignalState->num_child_flags数组尾部遍历,–slot为-1小于0,slot更新为PMSignalState->num_child_flags数组元素数量-1。如果该slot未使用则使用PM_CHILD_ASSIGNED,将PMSignalState->next_child_flag赋值为slot,并返回slot+1。
如果PMSignalState->next_child_flag不为0,进入for循环从PMSignalState->num_child_flags数组尾部遍历,–slot不小于0。如果该slot未使用则使用PM_CHILD_ASSIGNED,将PMSignalState->next_child_flag赋值为slot,并返回slot+1。

SendPostmasterSignal函数从子进程向postmaster发送信号,通过kill函数向postmaster发送SIGUSR1信号。支持如下PMSignalReason原由通知postmaster。

/* SendPostmasterSignal - signal the postmaster from a child process */
void SendPostmasterSignal(PMSignalReason reason) {/* If called in a standalone backend, do nothing */if (!IsUnderPostmaster) return;/* Atomically set the proper flag */PMSignalState->PMSignalFlags[reason] = true;/* Send signal to postmaster */kill(PostmasterPid, SIGUSR1);
}typedef enum {PMSIGNAL_RECOVERY_STARTED,	/* recovery has started */PMSIGNAL_BEGIN_HOT_STANDBY, /* begin Hot Standby */PMSIGNAL_WAKEN_ARCHIVER,	/* send a NOTIFY signal to xlog archiver */PMSIGNAL_ROTATE_LOGFILE,	/* send SIGUSR1 to syslogger to rotate logfile */PMSIGNAL_START_AUTOVAC_LAUNCHER,	/* start an autovacuum launcher */PMSIGNAL_START_AUTOVAC_WORKER,	/* start an autovacuum worker */PMSIGNAL_BACKGROUND_WORKER_CHANGE,	/* background worker state change */PMSIGNAL_START_WALRECEIVER, /* start a walreceiver */PMSIGNAL_ADVANCE_STATE_MACHINE, /* advance postmaster's state machine */NUM_PMSIGNALS				/* Must be last value of enum! */
} PMSignalReason;

在实现中,后台进程是这样通知Postmaster的:

  1. 首先在共享内存中开辟一个数组PMSignalFlags,数组中的每一位对应于一个信号。
  2. 然后如果后台进程希望向Postmaster发送一个信号,那么后台首先将信号在数组PMSignalFlags中相应的元素置1(逻辑真),然后调用kill函数向postmaster发送SIGUSR1信号。
  3. 当Postmaster收到SIGUSR1信号后首先检测共享存储中PMSingnalFlags,确认具体的信号是什么。同时将信号在数组PMSignalFlags中相应的元素置0(逻辑假)然后作出相应反应。

Postermaster向后端进程发信号

在这里插入图片描述

进程间信号通信

src/backend/storage/ipc/procsignal.c
通过传入MyBackendId获取ProcSignalSlots数组中的对应的元素槽,将信号槽中的pss_pid设置为MyProcPid,最后将该槽的指针赋值给MyProcSignalSlot。也就是一个后端一个slot,和门铃一样,其他进程可以敲击这个门铃slot来通知信号。

typedef struct {pid_t		pss_pid;sig_atomic_t pss_signalFlags[NUM_PROCSIGNALS];
} ProcSignalSlot;static ProcSignalSlot *ProcSignalSlots = NULL;
void ProcSignalInit(int pss_idx) {volatile ProcSignalSlot *slot;Assert(pss_idx >= 1 && pss_idx <= NumProcSignalSlots);slot = &ProcSignalSlots[pss_idx - 1];/* sanity check */if (slot->pss_pid != 0)elog(LOG, "process %d taking over ProcSignal slot %d, but it's not empty",MyProcPid, pss_idx);/* Clear out any leftover signal reasons */MemSet(slot->pss_signalFlags, 0, NUM_PROCSIGNALS * sizeof(sig_atomic_t));/* Mark slot with my PID */slot->pss_pid = MyProcPid;/* Remember slot location for CheckProcSignal */MyProcSignalSlot = slot;/* Set up to release the slot on process exit */on_shmem_exit(CleanupProcSignalState, Int32GetDatum(pss_idx));
}

在共享内存中创建ProcSignalSlot数组

/* ProcSignalShmemSize Compute space needed for procsignal's shared memory */
Size ProcSignalShmemSize(void) {return NumProcSignalSlots * sizeof(ProcSignalSlot);
}
/* ProcSignalShmemInit Allocate and initialize procsignal's shared memory */
void ProcSignalShmemInit(void) {Size		size = ProcSignalShmemSize();bool		found;ProcSignalSlots = (ProcSignalSlot *)ShmemInitStruct("ProcSignalSlots", size, &found);/* If we're first, set everything to zeroes */if (!found) MemSet(ProcSignalSlots, 0, size);
}

SendProcSignal向一个Postgres进程发送信号,如果信号发送出去了,即成功,返回0;错误返回-1并设置errno.

/* SendProcSignal Send a signal to a Postgres process* Providing backendId is optional, but it will speed up the operation.* On success (a signal was sent), zero is returned.* On error, -1 is returned, and errno is set (typically to ESRCH or EPERM).* Not to be confused with ProcSendSignal */
int SendProcSignal(pid_t pid, ProcSignalReason reason, BackendId backendId){volatile ProcSignalSlot *slot;if (backendId != InvalidBackendId){slot = &ProcSignalSlots[backendId - 1];/* Note: Since there's no locking, it's possible that the target* process detaches from shared memory and exits right after this* test, before we set the flag and send signal. And the signal slot* might even be recycled by a new process, so it's remotely possible* that we set a flag for a wrong process. That's OK, all the signals* are such that no harm is done if they're mistakenly fired. */if (slot->pss_pid == pid){/* Atomically set the proper flag */slot->pss_signalFlags[reason] = true;/* Send signal */return kill(pid, SIGUSR1);}}else{/* BackendId not provided, so search the array using pid.  We search* the array back to front so as to reduce search overhead.  Passing* InvalidBackendId means that the target is most likely an auxiliary* process, which will have a slot near the end of the array. */int			i;for (i = NumProcSignalSlots - 1; i >= 0; i--){slot = &ProcSignalSlots[i];if (slot->pss_pid == pid){/* the above note about race conditions applies here too *//* Atomically set the proper flag */slot->pss_signalFlags[reason] = true;/* Send signal */return kill(pid, SIGUSR1);}}}errno = ESRCH;return -1;
}

信号处理函数

/** procsignal_sigusr1_handler - handle SIGUSR1 signal.*/
void
procsignal_sigusr1_handler(SIGNAL_ARGS)
{int			save_errno = errno;if (CheckProcSignal(PROCSIG_CATCHUP_INTERRUPT))HandleCatchupInterrupt();if (CheckProcSignal(PROCSIG_NOTIFY_INTERRUPT))HandleNotifyInterrupt();if (CheckProcSignal(PROCSIG_PARALLEL_MESSAGE))HandleParallelMessageInterrupt();if (CheckProcSignal(PROCSIG_WALSND_INIT_STOPPING))HandleWalSndInitStopping();if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_TABLESPACE))RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_TABLESPACE);if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_LOCK))RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_LOCK);if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT))RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT);if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK))RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);SetLatch(MyLatch);latch_sigusr1_handler();errno = save_errno;
}

业界 RPC 框架大致分为两大流派,一种侧重跨语言调用,另一种是偏重服务治理。

跨语言调用型的 RPC 框架有 Thrift、gRPC、Hessian、Hprose 等。这类 RPC 框架侧重于服务的跨语言调用,能够支持大部分的语言进行语言无关的调用,非常适合多语言调用场景。但这类框架没有服务发现相关机制,实际使用时需要代理层进行请求转发和负载均衡策略控制。

其中,gRPC 是 Google 开发的高性能、通用的开源 RPC 框架,其由 Google 主要面向移动应用开发并基于 HTTP/2 协议标准而设计,基于 ProtoBuf(Protocol Buffers)序列化协议开发,且支持众多开发语言。本身它不是分布式的,所以要实现框架的功能需要进一步的开发。

Hprose(High Performance Remote Object Service Engine)是一个 MIT 开源许可的新型轻量级跨语言跨平台的面向对象的高性能远程动态通讯中间件。

服务治理型的 RPC 框架的特点是功能丰富,提供高性能的远程调用、服务发现及服务治理能力,适用于大型服务的服务解耦及服务治理,对于特定语言(Java)的项目可以实现透明化接入。缺点是语言耦合度较高,跨语言支持难度较大。国内常见的冶理型 RPC 框架如下:

Dubbo:Dubbo 是阿里巴巴公司开源的一个 Java 高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring 框架无缝集成。当年在淘宝内部,Dubbo 由于跟淘宝另一个类似的框架 HSF 有竞争关系,导致 Dubbo 团队解散,最近又活过来了,有专职同学投入。
DubboX:DubboX 是由当当在基于 Dubbo 框架扩展的一个 RPC 框架,支持 REST 风格的远程调用、Kryo/FST 序列化,增加了一些新的feature。Motan:Motan 是新浪微博开源的一个 Java 框架。它诞生的比较晚,起于 2013 年,2016 年 5 月开源。Motan 在微博平台中已经广泛应用,每天为数百个服务完成近千亿次的调用。
rpcx:rpcx 是一个类似阿里巴巴 Dubbo 和微博 Motan 的分布式的 RPC 服务框架,基于 Golang net/rpc 实现。但是 rpcx 基本只有一个人在维护,没有完善的社区,使用前要慎重,之前做 Golang 的 RPC 选型时也有考虑这个,最终还是放弃了,选择了 gRPC,如果想自己自研一个 RPC 框架,可以参考学习一下。

这篇关于PostgreSQL数据库PMsignal——后端进程\Postmaster信号通信的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/782878

相关文章

PostgreSQL的扩展dict_int应用案例解析

《PostgreSQL的扩展dict_int应用案例解析》dict_int扩展为PostgreSQL提供了专业的整数文本处理能力,特别适合需要精确处理数字内容的搜索场景,本文给大家介绍PostgreS... 目录PostgreSQL的扩展dict_int一、扩展概述二、核心功能三、安装与启用四、字典配置方法

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Java进程异常故障定位及排查过程

《Java进程异常故障定位及排查过程》:本文主要介绍Java进程异常故障定位及排查过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、故障发现与初步判断1. 监控系统告警2. 日志初步分析二、核心排查工具与步骤1. 进程状态检查2. CPU 飙升问题3. 内存

MySQL数据库中ENUM的用法是什么详解

《MySQL数据库中ENUM的用法是什么详解》ENUM是一个字符串对象,用于指定一组预定义的值,并可在创建表时使用,下面:本文主要介绍MySQL数据库中ENUM的用法是什么的相关资料,文中通过代码... 目录mysql 中 ENUM 的用法一、ENUM 的定义与语法二、ENUM 的特点三、ENUM 的用法1

Java中调用数据库存储过程的示例代码

《Java中调用数据库存储过程的示例代码》本文介绍Java通过JDBC调用数据库存储过程的方法,涵盖参数类型、执行步骤及数据库差异,需注意异常处理与资源管理,以优化性能并实现复杂业务逻辑,感兴趣的朋友... 目录一、存储过程概述二、Java调用存储过程的基本javascript步骤三、Java调用存储过程示

Go语言数据库编程GORM 的基本使用详解

《Go语言数据库编程GORM的基本使用详解》GORM是Go语言流行的ORM框架,封装database/sql,支持自动迁移、关联、事务等,提供CRUD、条件查询、钩子函数、日志等功能,简化数据库操作... 目录一、安装与初始化1. 安装 GORM 及数据库驱动2. 建立数据库连接二、定义模型结构体三、自动迁

嵌入式数据库SQLite 3配置使用讲解

《嵌入式数据库SQLite3配置使用讲解》本文强调嵌入式项目中SQLite3数据库的重要性,因其零配置、轻量级、跨平台及事务处理特性,可保障数据溯源与责任明确,详细讲解安装配置、基础语法及SQLit... 目录0、惨痛教训1、SQLite3环境配置(1)、下载安装SQLite库(2)、解压下载的文件(3)、

MySQL数据库的内嵌函数和联合查询实例代码

《MySQL数据库的内嵌函数和联合查询实例代码》联合查询是一种将多个查询结果组合在一起的方法,通常使用UNION、UNIONALL、INTERSECT和EXCEPT关键字,下面:本文主要介绍MyS... 目录一.数据库的内嵌函数1.1聚合函数COUNT([DISTINCT] expr)SUM([DISTIN

MySQL追踪数据库表更新操作来源的全面指南

《MySQL追踪数据库表更新操作来源的全面指南》本文将以一个具体问题为例,如何监测哪个IP来源对数据库表statistics_test进行了UPDATE操作,文内探讨了多种方法,并提供了详细的代码... 目录引言1. 为什么需要监控数据库更新操作2. 方法1:启用数据库审计日志(1)mysql/mariad

postgresql数据库基本操作及命令详解

《postgresql数据库基本操作及命令详解》本文介绍了PostgreSQL数据库的基础操作,包括连接、创建、查看数据库,表的增删改查、索引管理、备份恢复及退出命令,适用于数据库管理和开发实践,感兴... 目录1. 连接 PostgreSQL 数据库2. 创建数据库3. 查看当前数据库4. 查看所有数据库