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

相关文章

SpringBoot实现数据库读写分离的3种方法小结

《SpringBoot实现数据库读写分离的3种方法小结》为了提高系统的读写性能和可用性,读写分离是一种经典的数据库架构模式,在SpringBoot应用中,有多种方式可以实现数据库读写分离,本文将介绍三... 目录一、数据库读写分离概述二、方案一:基于AbstractRoutingDataSource实现动态

C# WinForms存储过程操作数据库的实例讲解

《C#WinForms存储过程操作数据库的实例讲解》:本文主要介绍C#WinForms存储过程操作数据库的实例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、存储过程基础二、C# 调用流程1. 数据库连接配置2. 执行存储过程(增删改)3. 查询数据三、事务处

mysql数据库重置表主键id的实现

《mysql数据库重置表主键id的实现》在我们的开发过程中,难免在做测试的时候会生成一些杂乱无章的SQL主键数据,本文主要介绍了mysql数据库重置表主键id的实现,具有一定的参考价值,感兴趣的可以了... 目录关键语法演示案例在我们的开发过程中,难免在做测试的时候会生成一些杂乱无章的SQL主键数据,当我们

Spring Boot 整合 MyBatis 连接数据库及常见问题

《SpringBoot整合MyBatis连接数据库及常见问题》MyBatis是一个优秀的持久层框架,支持定制化SQL、存储过程以及高级映射,下面详细介绍如何在SpringBoot项目中整合My... 目录一、基本配置1. 添加依赖2. 配置数据库连接二、项目结构三、核心组件实现(示例)1. 实体类2. Ma

Linux中的进程间通信之匿名管道解读

《Linux中的进程间通信之匿名管道解读》:本文主要介绍Linux中的进程间通信之匿名管道解读,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、基本概念二、管道1、温故知新2、实现方式3、匿名管道(一)管道中的四种情况(二)管道的特性总结一、基本概念我们知道多

查看Oracle数据库中UNDO表空间的使用情况(最新推荐)

《查看Oracle数据库中UNDO表空间的使用情况(最新推荐)》Oracle数据库中查看UNDO表空间使用情况的4种方法:DBA_TABLESPACES和DBA_DATA_FILES提供基本信息,V$... 目录1. 通过 DBjavascriptA_TABLESPACES 和 DBA_DATA_FILES

Linux进程终止的N种方式详解

《Linux进程终止的N种方式详解》进程终止是操作系统中,进程的一个重要阶段,他标志着进程生命周期的结束,下面小编为大家整理了一些常见的Linux进程终止方式,大家可以根据需求选择... 目录前言一、进程终止的概念二、进程终止的场景三、进程终止的实现3.1 程序退出码3.2 运行完毕结果正常3.3 运行完毕

Java实现数据库图片上传与存储功能

《Java实现数据库图片上传与存储功能》在现代的Web开发中,上传图片并将其存储在数据库中是常见的需求之一,本文将介绍如何通过Java实现图片上传,存储到数据库的完整过程,希望对大家有所帮助... 目录1. 项目结构2. 数据库表设计3. 实现图片上传功能3.1 文件上传控制器3.2 图片上传服务4. 实现

使用Dify访问mysql数据库详细代码示例

《使用Dify访问mysql数据库详细代码示例》:本文主要介绍使用Dify访问mysql数据库的相关资料,并详细讲解了如何在本地搭建数据库访问服务,使用ngrok暴露到公网,并创建知识库、数据库访... 1、在本地搭建数据库访问的服务,并使用ngrok暴露到公网。#sql_tools.pyfrom

Java实现数据库图片上传功能详解

《Java实现数据库图片上传功能详解》这篇文章主要为大家详细介绍了如何使用Java实现数据库图片上传功能,包含从数据库拿图片传递前端渲染,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、前言2、数据库搭建&nbsChina编程p; 3、后端实现将图片存储进数据库4、后端实现从数据库取出图片给前端5、前端拿到