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

相关文章

Java进程CPU使用率过高排查步骤详细讲解

《Java进程CPU使用率过高排查步骤详细讲解》:本文主要介绍Java进程CPU使用率过高排查的相关资料,针对Java进程CPU使用率高的问题,我们可以遵循以下步骤进行排查和优化,文中通过代码介绍... 目录前言一、初步定位问题1.1 确认进程状态1.2 确定Java进程ID1.3 快速生成线程堆栈二、分析

RabbitMQ工作模式中的RPC通信模式详解

《RabbitMQ工作模式中的RPC通信模式详解》在RabbitMQ中,RPC模式通过消息队列实现远程调用功能,这篇文章给大家介绍RabbitMQ工作模式之RPC通信模式,感兴趣的朋友一起看看吧... 目录RPC通信模式概述工作流程代码案例引入依赖常量类编写客户端代码编写服务端代码RPC通信模式概述在R

在Spring Boot中实现HTTPS加密通信及常见问题排查

《在SpringBoot中实现HTTPS加密通信及常见问题排查》HTTPS是HTTP的安全版本,通过SSL/TLS协议为通讯提供加密、身份验证和数据完整性保护,下面通过本文给大家介绍在SpringB... 目录一、HTTPS核心原理1.加密流程概述2.加密技术组合二、证书体系详解1、证书类型对比2. 证书获

Druid连接池实现自定义数据库密码加解密功能

《Druid连接池实现自定义数据库密码加解密功能》在现代应用开发中,数据安全是至关重要的,本文将介绍如何在​​Druid​​连接池中实现自定义的数据库密码加解密功能,有需要的小伙伴可以参考一下... 目录1. 环境准备2. 密码加密算法的选择3. 自定义 ​​DruidDataSource​​ 的密码解密3

Maven项目中集成数据库文档生成工具的操作步骤

《Maven项目中集成数据库文档生成工具的操作步骤》在Maven项目中,可以通过集成数据库文档生成工具来自动生成数据库文档,本文为大家整理了使用screw-maven-plugin(推荐)的完... 目录1. 添加插件配置到 pom.XML2. 配置数据库信息3. 执行生成命令4. 高级配置选项5. 注意事

在Java中基于Geotools对PostGIS数据库的空间查询实践教程

《在Java中基于Geotools对PostGIS数据库的空间查询实践教程》本文将深入探讨这一实践,从连接配置到复杂空间查询操作,包括点查询、区域范围查询以及空间关系判断等,全方位展示如何在Java环... 目录前言一、相关技术背景介绍1、评价对象AOI2、数据处理流程二、对AOI空间范围查询实践1、空间查

Python+PyQt5实现MySQL数据库备份神器

《Python+PyQt5实现MySQL数据库备份神器》在数据库管理工作中,定期备份是确保数据安全的重要措施,本文将介绍如何使用Python+PyQt5开发一个高颜值,多功能的MySQL数据库备份工具... 目录概述功能特性核心功能矩阵特色功能界面展示主界面设计动态效果演示使用教程环境准备操作流程代码深度解

MySQL数据库实现批量表分区完整示例

《MySQL数据库实现批量表分区完整示例》通俗地讲表分区是将一大表,根据条件分割成若干个小表,:本文主要介绍MySQL数据库实现批量表分区的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考... 目录一、表分区条件二、常规表和分区表的区别三、表分区的创建四、将既有表转换分区表脚本五、批量转换表为分区

Python模拟串口通信的示例详解

《Python模拟串口通信的示例详解》pySerial是Python中用于操作串口的第三方模块,它支持Windows、Linux、OSX、BSD等多个平台,下面我们就来看看Python如何使用pySe... 目录1.win 下载虚www.chinasem.cn拟串口2、确定串口号3、配置串口4、串口通信示例5

Python多进程、多线程、协程典型示例解析(最新推荐)

《Python多进程、多线程、协程典型示例解析(最新推荐)》:本文主要介绍Python多进程、多线程、协程典型示例解析(最新推荐),本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定... 目录一、multiprocessing(多进程)1. 模块简介2. 案例详解:并行计算平方和3. 实现逻