PostgreSQL可以扩展为在单独的进程中运行用户提供的代码。命令postgres启动、 停止和监控这样的进程,允许它们的生命周期与服务器状态紧密关联。这些进程可以选择连接 PostgreSQL的共享内存并与数据库内部连接;它们也可以串行地运行多个事务,就像 常规的客户端连接的服务器进程。另外,通过链接到libpq,它们可以连接到服务器并 且和常规的客户端应用表现一样。
警告 |
使用后台工作进程有着相当大的鲁棒性和安全风险,这是因为它们是用C语言写的,有着不受限制的数据 访问方式。乐于使用包含后台工作进程的模块的管理员们应当极度地当心。只有仔细审计过的模块才 应该被允许运行后台工作进程。 |
通过在shared_preload_libraries中包含模块名,后台工作程序可以在
PostgreSQL启动时安装。想要运行后台工作程序的模块可以通过从它的
_PG_init()
调用
RegisterBackgroundWorker(BackgroundWorker *worker)
来注册这个程序。后台工作程序也可以在系统启动之后启动,通过调用函数
RegisterDynamicBackgroundWorker(BackgroundWorker
*worker, BackgroundWorkerHandle **handle)
来运行。
不像RegisterBackgroundWorker
,只能在主进程中调用,
RegisterDynamicBackgroundWorker
必须从一个普通后端中调用。
BackgroundWorker结构是这样定义的:
typedef void (*bgworker_main_type)(Datum main_arg); typedef struct BackgroundWorker { char bgw_name[BGW_MAXLEN]; int bgw_flags; BgWorkerStartTime bgw_start_time; int bgw_restart_time; /* in seconds, or BGW_NEVER_RESTART */ bgworker_main_type bgw_main; char bgw_library_name[BGW_MAXLEN]; /* only if bgw_main is NULL */ char bgw_function_name[BGW_MAXLEN]; /* only if bgw_main is NULL */ Datum bgw_main_arg; int bgw_notify_pid; } BackgroundWorker;
bgw_name是用于日志消息、进程列表和类似环境的一个字符串。
bgw_flags是一个按位与的比特掩码,显示模块想要的容量。可能的值有 BGWORKER_SHMEM_ACCESS (要求访问共享内存)和 BGWORKER_BACKEND_DATABASE_CONNECTION(要求能够建立一个数据库连接,通 过这个连接稍后可以运行事务和查询)。一个使用 BGWORKER_BACKEND_DATABASE_CONNECTION连接数据库的后台工作程序还必须用 BGWORKER_SHMEM_ACCESS连接共享内存,否则程序的启动会失败。
bgw_start_time是某种服务器状态,在此期间应当由 postgres启动进程;它可以是下面几个值之一: BgWorkerStart_PostmasterStart(postgres完成自身初始化后就立即启动; 请求此种启动方式的进程不能进行数据库连接),BgWorkerStart_ConsistentState( 一旦在热备份系统中达到了一致状态就启动,允许进程连接到数据库并运行只读查询),以及 BgWorkerStart_RecoveryFinished(只要系统进入普通读写状态就启动)。注意在非热备 份系统的服务器中后两个值是作用相当的。注意此设置只在进程将启动时显示;进入不同状态时进程不停。
bgw_restart_time是以秒记的时间间隔。一旦进程崩溃, postgres应当在重启进程前等待这一段时间。它可以是任何的正值,或者 是BGW_NEVER_RESTART以表明在进程崩溃时不重启进程。
bgw_main是当进程被启动时指向所运行函数的一个指针。该函数必须使 用Datum类型的单一参数并返回void类型的值。 bgw_main_arg将作为唯一参数被传递给此函数。注意全局变量 MyBgworkerEntry指向进程注册时传递的 BackgroundWorker结构的一份拷贝。 bgw_main可能是NULL;在这种情况下, bgw_library_name和bgw_function_name 将会用来决定入口点。这对于在主进程启动之后加载的后台工作程序来说是有用的, 这时主进程还没有加载必需的库。
bgw_library_name是一个库的名字, 是后台工作程序应该寻找的初始入口点。忽略它,除非bgw_main 是NULL。但是如果bgw_main是NULL, 那么命名的库将通过工作进程动态的加载,并且bgw_function_name 将用来辨认要被调用的函数。
bgw_function_name是一个函数的名字, 在一个应该用作一个新的后台工作程序的初始入口点的动态加载库中。 忽略它,除非bgw_main是NULL。
bgw_notify_pid是一个PostgreSQL后台程序的PID, 在该进程启动或退出时,主进程应该发送SIGUSR1。 进程是在主进程启动时注册的,或者后端注册该进程,并不希望等待该进程启动时, 它应该是0。否则,它应该被初始化为MyProcPid。
进程一旦运行,就可以通过调用
BackgroundWorkerInitializeConnection(char *dbname, char *username)
连接到一个数据库。这样进程可以使用SPI接口运行事务和查询。如果
dbname值为NULL,会话不被连接到任何特定数据库,但是可以获取共享的目录。如果
username值为NULL,进程会以initdb运行时创建的超级用户身份来运行。
每个后台进程只能调用一次BackgroundWorkerInitializeConnection,它不能切换数据库。
控制到达bgw_main函数时信号是锁定的,并且必须由此函数解锁;这样在必要时可以允许
进程定制它的信号处理程序。通过调用BackgroundWorkerUnblockSignals
可以在新进程
中为信号解锁,通过调用BackgroundWorkerBlockSignals
可以加锁。
如果一个后台工作程序的bgw_restart_time被配置为
BGW_NEVER_RESTART,或者如果它带有退出代码0退出或被
TerminateBackgroundWorker
终止,那么它将在通过
bgw_restart_time配置之后重启,
或者如果主进程由于后端失败重新初始化集群时立即重启。
需要暂停执行的后端应该只是临时使用一个可中断的睡眠,而不是退出;
通过调用WaitLatch()
可以实现这
一点。确定调用该函数时设置了WL_POSTMASTER_DEATH标志,
并且在postgres自身终止的紧急情况下为提示的退出验证返回码。
当一个后台工作程序是用RegisterDynamicBackgroundWorker
函数注册的时,该后端执行注册获取信息而不管进程的状态是可能的。
想要这样做的后端应该传递BackgroundWorkerHandle *
的地址作为第二个参数到RegisterDynamicBackgroundWorker
。
如果该进程成功的注册了,那么这个指针将被初始化为一个不透明的句柄,
稍后被传递到GetBackgroundWorkerPid(BackgroundWorkerHandle *、
pid_t *)
或
TerminateBackgroundWorker(BackgroundWorkerHandle *)
。
GetBackgroundWorkerPid
可以用来轮询该进程的状态:
BGWH_NOT_YET_STARTED的一个返回值表明该进程尚未被主进程启动;
BGWH_STOPPED表明它已经启动了但是不再运行了;
BGWH_STARTED表明它当前正在运行。在最后一种情况下,
PID将仍然通过第二个参数返回。TerminateBackgroundWorker
导致主进程发送SIGTERM到该进程,如果它正在运行的话,
并且尽快的注销它。
在某些情况下,一个注册后台工作程序的进程可能希望等待该后台工作程序启动。
这可以通过初始化bgw_notify_pid为
MyProcPid,并且然后传递在注册时获得的
BackgroundWorkerHandle *到
WaitForBackgroundWorkerStartup(BackgroundWorkerHandle
*handle, pid_t *)
函数完成。
这个函数将锁定,直到主进程尝试启动该后台工作程序,或者直到主进程死亡。
如果该后端正在运行,那么返回值将是BGWH_STARTED,
并且PID将被写入提供的地址。否则,返回值将是BGWH_STOPPED
或BGWH_POSTMASTER_DIED。
worker_spi contrib模块包含一个展示一些有用技术的范例。
注册后台工作程序的最大数量是通过max_worker_processes来限制的。