## 第 48 章后台工作进程 [](<>) PostgreSQL 可以扩展为在单独的进程中运行用户提供的代码。此类进程由以下人员启动、停止和监控`postgres`,这允许它们的生命周期与服务器的状态密切相关。这些进程可以选择附加到 PostgreSQL 的共享内存区域并在内部连接到数据库;它们还可以串行运行多个事务,就像常规的客户端连接的服务器进程一样。此外,通过链接到 libpq,它们可以连接到服务器并像常规客户端应用程序一样运行。 ### 警告 使用后台工作进程存在相当大的健壮性和安全风险,因为编写在`C`语言,他们可以不受限制地访问数据。希望启用包含后台工作进程的模块的管理员应格外小心。只有经过仔细审核的模块才能被允许运行后台工作进程。 后台工作人员可以在 PostgreSQL 启动时通过在中包含模块名称来初始化`shared_preload_libraries`.希望运行后台工作者的模块可以通过调用来注册它`注册后台工作者(`后台工作者`**`工人`*)`从其`_PG_init()`功能。后台工作人员也可以在系统启动并运行后通过调用启动`RegisterDynamicBackgroundWorker(`后台工作者`**`工人`*,`背景工作者句柄`***`处理`*)`.不像`注册BackgroundWorker`,只能从 postmaster 进程中调用,`注册DynamicBackgroundWorker`必须从常规后端或其他后台工作人员调用。 结构`后台工作者`是这样定义的: ``` typedef void (*bgworker_main_type)(Datum main_arg); typedef struct BackgroundWorker { char bgw_name[BGW_MAXLEN]; char bgw_type[BGW_MAXLEN]; int bgw_flags; BgWorkerStartTime bgw_start_time; int bgw_restart_time; /* in seconds, or BGW_NEVER_RESTART */ char bgw_library_name[BGW_MAXLEN]; char bgw_function_name[BGW_MAXLEN]; Datum bgw_main_arg; char bgw_extra[BGW_EXTRALEN]; int bgw_notify_pid; } BackgroundWorker; ``` `bgw_name`和`bgw_type`是在日志消息、进程列表和类似上下文中使用的字符串。`bgw_type`对于相同类型的所有后台工作人员应该是相同的,以便可以将这些工作人员分组到一个进程列表中,例如。`bgw_name`另一方面,可以包含有关特定过程的附加信息。(通常,字符串为`bgw_name`将以某种方式包含类型,但这不是严格要求的。) `bgw_flags`是一个位或位掩码,指示模块想要的功能。可能的值为: `BGWORKER_SHMEM_ACCESS` [](<>)请求共享内存访问。没有共享内存访问权限的工作人员无法访问任何 PostgreSQL 的共享数据结构,例如重量级或轻量级锁、共享缓冲区或工作人员本身可能希望创建和使用的任何自定义数据结构。 `BGWORKER_BACKEND_DATABASE_CONNECTION` [](<>)请求建立数据库连接的能力,以后可以通过该连接运行事务和查询。后台工作人员使用`BGWORKER_BACKEND_DATABASE_CONNECTION`连接到数据库还必须使用附加共享内存`BGWORKER_SHMEM_ACCESS`,否则worker启动将失败。 `bgw_start_time`是服务器状态,在此期间`postgres`应该开始这个过程;它可以是其中之一`BgWorkerStart_PostmasterStart`(尽快开始`postgres`本身已经完成了自己的初始化;请求这个的进程不符合数据库连接的条件),`BgWorkerStart_ConsistentState`(在热备用中达到一致状态后立即启动,允许进程连接到数据库并运行只读查询),以及`BgWorkerStart_RecoveryFinished`(系统进入正常读写状态后立即启动)。请注意,最后两个值在非热备用服务器中是等效的。请注意,此设置仅指示何时启动进程;当达到不同的状态时,它们不会停止。 `bgw_restart_time`是间隔,以秒为单位,`postgres`如果它崩溃,应该在重新启动进程之前等待。它可以是任何正值,或者`BGW_NEVER_RESTART`, 表示在崩溃的情况下不重新启动进程。 `bgw_library_name`是应在其中寻找后台工作人员的初始入口点的库的名称。命名库将由工作进程动态加载,并且`bgw_function_name`将用于标识要调用的函数。如果从核心代码加载函数,则必须将其设置为“postgres”。 `bgw_function_name`是动态加载的库中的函数名称,应用作新后台工作程序的初始入口点。 `bgw_main_arg`是个`基准`后台工作主函数的参数。这个主函数应该接受一个类型的参数`基准`并返回`空白`.`bgw_main_arg`将作为参数传递。此外,全局变量`MyBgworkerEntry`指向副本`后台工作者`注册时通过的结构;工人可能会发现检查这种结构很有帮助。 在 Windows 上(以及其他任何地方`EXEC_BACKEND`已定义)或在动态后台工作人员中,通过`基准`通过参考,仅按价值。如果需要参数,最安全的做法是传递一个 int32 或其他小值并将其用作共享内存中分配的数组的索引。如果像一个值`字符串`或者`文本`传递,则指针在新的后台工作进程中将无效。 `bgw_extra`可以包含要传递给后台工作人员的额外数据。不像`bgw_main_arg`,这个数据不会作为参数传递给worker的main函数,但是可以通过`MyBgworkerEntry`,如上所述。 `bgw_notify_pid`是 postmaster 应该发送到的 PostgreSQL 后端进程的 PID`SIGUSR1`当进程启动或退出时。对于在 postmaster 启动时注册的 worker,或者当注册 worker 的后端不希望等待 worker 启动时,它应该为 0.否则,它应该被初始化为`MyProcPid`. 运行后,该进程可以通过调用连接到数据库`BackgroundWorkerInitializeConnection(*`字符*数据库名\`*,*\`字符*用户名`*, *`uint32 标志`*)`要么`BackgroundWorkerInitializeConnectionByOid(*`椭圆形`*, *`类用户类`*, *`uint32 标志`*)`.这允许进程使用`SPI`界面。如果`数据库名称`为 NULL 或`双胞胎`是`无效`,会话没有连接到任何特定的数据库,但可以访问共享目录。如果`用户名`为 NULL 或`用户类`是`无效`, 该进程将作为创建的超级用户运行`初始化数据库`.如果`BGWORKER_BYPASS_ALLOWCONN`被指定为`旗帜`可以绕过限制连接到不允许用户连接的数据库。后台工作人员只能调用这两个函数之一,并且只能调用一次。无法切换数据库。 当控制到达后台worker的main函数时,信号最初被阻塞,并且必须被它解除阻塞;这是为了允许进程在必要时自定义其信号处理程序。信号可以通过调用在新进程中解除阻塞`BackgroundWorkerUnblockSignals`并通过调用阻止`背景工作者块信号`. 如果`bgw_restart_time`后台工作人员配置为`BGW_NEVER_RESTART`,或者如果它以退出代码 0 退出或被终止`TerminateBackgroundWorker`,它会在退出时被邮局管理员自动注销。否则,它将在通过配置的时间段后重新启动`bgw_restart_time`,或者如果 postmaster 由于后端故障重新初始化集群,则立即。只需要暂时暂停执行的后端应该使用可中断的睡眠而不是退出;这可以通过调用来实现`等待闩锁()`.确保`WL_POSTMASTER_DEATH`调用该函数时设置标志,并验证在紧急情况下提示退出的返回码`postgres`本身已经终止。 当后台工作人员使用`注册DynamicBackgroundWorker`功能,执行注册的后端可以获取有关工人状态的信息。希望这样做的后端应该传递一个地址`背景工作者句柄 *`作为第二个论点`注册DynamicBackgroundWorker`.如果工人注册成功,这个指针将被初始化一个不透明的句柄,随后可以传递给`GetBackgroundWorkerPid(*`背景工作者句柄*\`*,*\`pid_t*`*)`或者`终止BackgroundWorker(*`背景工作者句柄*\`*)`.`获取BackgroundWorkerPid`可用于轮询worker的状态:返回值为`BGWH_NOT_YET_STARTED`表示邮递员还没有启动worker;`BGWH_STOPPED`表示已经启动但不再运行;和`BGWH_STARTED`表示当前正在运行。在最后一种情况下,PID 也将通过第二个参数返回。`TerminateBackgroundWorker`导致邮局局长发送`如果它正在运行,则向工作人员发送 SIGTERM\`,并在它不运行时立即取消注册。 在某些情况下,注册后台工作者的进程可能希望等待工作者启动。这可以通过初始化来完成`bgw_notify_pid`到`MyProcPid`然后通过`背景工作者句柄 *`在注册时获得`WaitForBackgroundWorkerStartup(*`背景工作者句柄*句柄\`*,*\`pid_t*`*)`功能。此函数将一直阻塞,直到 postmaster 尝试启动后台工作程序,或者直到 postmaster 死亡。如果后台工作人员正在运行,则返回值为`BGWH_STARTED`, 并且 PID 将被写入提供的地址。否则,返回值将是`BGWH_STOPPED`要么`BGWH_POSTMASTER_DIED`. 进程也可以通过使用`WaitForBackgroundWorkerShutdown(*`背景工作者句柄*句柄\`*)`函数并传递`背景工作者句柄 \*`注册时获得。此函数将阻塞,直到后台工作人员退出或 postmaster 死亡。后台worker退出时,返回值为`BGWH_STOPPED`, 如果 postmaster 死了,它会返回`BGWH_POSTMASTER_DIED\`。 后台工作人员可以发送异步通知消息,或者通过使用`通知`通过 SPI 命令,或直接通过`Async_Notify()`.此类通知将在事务提交时发送。后台工作人员不应注册以接收异步通知`听`命令,因为没有基础设施可供工作人员使用此类通知。 这`src/test/modules/worker_spi`模块包含一个工作示例,它演示了一些有用的技术。 注册后台工作人员的最大数量受限于[最大限度\_工人\_流程](runtime-config-resource.html#GUC-MAX-WORKER-PROCESSES).