四月 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
才能生效。这些是
hugepage
libev
log_path
log_type
max_connections
pipeline
unix_socket_dir
pidfile
- 由
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
中完成。