四月 11, 2024
摘要:pgagroal是一个 PostgreSQL 的高性能连接池。
目录
概述
pgagroal使用进程模型(fork()),其中每个进程处理一个到 PostgreSQL 的连接。这样做是为了确保,在一个连接上发生崩溃不会导致整个池关闭。
主进程定义在main.c中。当客户端连接时,它会在自己的进程中进行处理,处理逻辑的实现在worker.h(worker.c)中。
一旦客户端断开连接,连接会被放回池中,子进程被终止。
共享内存
有一块内存段(shmem.h)在所有进程之间共享,其中包含了pgagroal状态,该状态包含池的配置、服务器列表和每个连接的状态。
在该共享内存段中会初始化pgagroal的配置(struct configuration)、服务器的配置(struct server)和每个连接的状态(struct connection)。这些结构都定义在pgagroal.h中。
共享内存段通过调用mmap()进行创建。
原子操作
原子操作库用于定义每个连接的状态,并按连接状态图来调整它们。状态图具有以下状态
| 状态名称 | 描述 |
|---|---|
STATE_NOTINIT |
连接尚未初始化 |
STATE_INIT |
连接正在初始化 |
STATE_FREE |
连接是空闲的 |
STATE_IN_USE |
连接正在使用中 |
STATE_GRACEFULLY |
返回池时,连接将被终止 |
STATE_FLUSH |
连接正在刷新 |
STATE_IDLE_CHECK |
正在检查连接空闲超时 |
STATE_MAX_CONNECTION_AGE |
正在检查连接的最长连接期限 |
STATE_VALIDATION |
正在验证连接 |
STATE_REMOVE |
连接正在被删除 |
这些状态定义在pgagroal.h中。
连接池
pgagroal连接池的 API 定义在pool.h(pool.c)中。
此 API 定义了池的功能,例如从池中获取连接并返回。进程之间没有顺序,因此新创建的进程可以在旧进程之前获得连接。
连接池在struct connection数据类型上的操作,定义在pgagroal.h中。
网络和消息
所有通信都使用message.h中定义的struct message数据类型进行了抽象。
读取和写出消息,在message.h(message.c)文件中处理。
网络操作定义在network.h(network.c)中。
内存
每个进程都使用一个固定的内存块进行网络通信,该内存块在工作进程启动时分配。
这样,我们就不必为每条网络消息分配内存,更重要的是还要在使用结束后释放它。
内存接口定义在memory.h(memory.c)中。
管理
pgagroal有一个管理接口,用于两个目的。
首先,它定义了在池运行时可以对池执行的管理员功能。这包括例如刷新连接池。pgagroal-cli程序可用于这些操作(cli.c)。
第二,在创建新连接后,该接口在内部用于将连接(套接字描述符)从子进程传输到pgagroal主进程。这是必要的,因为套接字描述符还需要提供给后续的客户端和进程。
管理接口采用 Unix 域套接字进行通信。
管理接口定义在management.h中。管理接口使用自己的协议,该协议始终会包含一个报头
| 字段 | 类型 | 描述 |
|---|---|---|
id |
Byte | 消息类型的标识符 |
slot |
Int | 消息所对应的槽位 |
消息的其余部分取决于消息类型。
远程管理
远程管理功能使用与标准管理方法相同的协议。
但是,在发送管理数据包之前,客户端必须使用与 PostgreSQL 相同的消息格式,使用 SCRAM-SHA-256 进行身份验证,例如 StartupMessage、AuthenticationSASL、AuthenticationSASLContinue、AuthenticationSASLFinal 和 AuthenticationOk。支持 SSLRequest 消息。
远程管理接口定义在remote.h(remote.c)中。
libev 的应用
libev 用于处理网络交互,该交互在EV_READ事件发生时被“激活”。
每个进程都有自己的事件循环,因此,只有当仅与该进程相关的数据准备就绪时,该进程才会收到通知。主循环会处理系统范围的“服务”,例如空闲超时检查等。
管道
pgagroal有管道的概念,定义了通信如何从客户端通过pgagroal路由到 PostgreSQL。在另一个方向上也是如此。
管道在pipeline.h中定义为:
struct pipeline
{
initialize initialize;
start start;
callback client;
callback server;
stop stop;
destroy destroy;
periodic periodic;
};
管道中的函数定义为
| 函数 | 描述 |
|---|---|
initialize |
管道的全局初始化,会返回一个指向共享内存段的指针 |
start |
在管道实例启动时调用 |
client |
客户端到pgagroal的通信 |
server |
PostgreSQL 到pgagroal的通信 |
stop |
在管道实例停止时调用 |
destroy |
管道的全局销毁 |
periodic |
周期性调用 |
函数start、client、server和stop可以访问以下信息
struct worker_io
{
struct ev_io io; /* The libev base type */
int client_fd; /* The client descriptor */
int server_fd; /* The server descriptor */
int slot; /* The slot */
SSL* client_ssl; /* The client SSL context */
SSL* server_ssl; /* The server SSL context */
};
在worker.h中定义。
性能管道
pgagroal 的目标之一是性能,因此性能管道仅会关注来自客户端的 Terminate 消息,并对其进行操作。同样,性能管道只会关注来自服务器的FATAL错误。这使得管道非常快,因为在交互中只有极小的开销。
管道定义在pipeline_perf.c中的函数中
| 函数 | 描述 |
|---|---|
performance_initialize |
无 |
performance_start |
无 |
performance_client |
客户端到pgagroal的通信 |
performance_server |
PostgreSQL 到pgagroal的通信 |
performance_stop |
无 |
performance_destroy |
无 |
performance_periodic |
无 |
会话管道
会话管道的工作方式与性能管道类似,不同之处在于它会检查是否应使用传输层安全协议(TLS)进行传输。
管道定义在pipeline_session.c中的函数中
| 函数 | 描述 |
|---|---|
session_initialize |
如果 disconnect_client 处于活动状态,则初始化内存段 |
session_start |
如果 disconnect_client 处于活动状态,则准备客户端段 |
session_client |
客户端到pgagroal的通信 |
session_server |
PostgreSQL 到pgagroal的通信 |
session_stop |
如果 disconnect_client 处于活动状态,则更新客户端段 |
session_destroy |
如果已初始化,则销毁内存段 |
session_periodic |
检查是否应断开客户端连接 |
事务管道
事务管道会在每次事务后,返回与服务器的连接。管道支持传输层安全协议(TLS)。
管道使用 ReadyForQuery 消息来检查事务的状态,因此需要维护对消息报头的跟踪。
管道有一个管理接口,以便在将新连接添加到连接池时,从父进程接收套接字描述符。如果相关客户端不认为套接字描述符有效,则连接池会重试。
管道定义在pipeline_transaction.c中的函数中
| 函数 | 描述 |
|---|---|
transaction_initialize |
无 |
transaction_start |
设置进程变量,并返回池中的连接 |
transaction_client |
客户端到pgagroal的通信。需要时会获取连接 |
transaction_server |
PostgreSQL 到pgagroal的通信。跟踪消息报头 |
transaction_stop |
如果需要,将连接返回到池。活动事务可能会回滚 |
transaction_destroy |
无 |
transaction_periodic |
无 |
信号
pgagroal的主进程支持信号SIGTERM、SIGINT和SIGALRM作为一种关闭机制。SIGTRAP信号将使pgagroal正常关闭,这意味着允许正在访问的连接完成其会话。SIGABRT用于请求核心转储(abort())。SIGHUP信号将触发重新加载配置。
子进程支持SIGQUIT作为一种关闭机制。这不会关闭连接池本身。
不需要将SIGKILL用于pgagroal。请考虑使用SIGABRT,并与pgagroal社区共享核心转储和调试日志。
重新加载
SIGHUP信号会触发重新加载配置。
但是,某些配置的设置需要完全重启pgagroal才能生效。这些是
hugepagelibevlog_pathlog_typemax_connectionspipelineunix_socket_dirpidfile- 由
pgagroal_databases.conf定义的限制规则 - 服务器部分定义的 TLS 规则
也可以使用pgagroal-cli -c pgagroal.conf reload来重新加载配置。该命令仅在本地接口上受支持,因此不能远程工作。
Prometheus
pgagroal 在指定metrics端口时支持 Prometheus。
该模块服务于两个端口
/- 功能概述 (text/html)/metrics- 指标 (text/plain)
访问所有其他 URL 都会出现 403 响应。
指标端口支持Transfer-Encoding: chunked来处理大量数据。
实现在prometheus.h和prometheus.c中完成。
故障转移支持
pgagroal 可以在客户端无法写入的情况下,对 PostgreSQL 实例进行故障转移。
这是使用用户提供的外部脚本完成的。
实现在server.h和server.c中完成。
日志记录
基于atomic_schar锁实现的简单日志模块。
实现在logging.h和logging.c中完成。