pgagroal: 架构

四月 11, 2024

摘要pgagroal是一个 PostgreSQL 的高性能连接池。

目录

概述

pgagroal使用进程模型(fork()),其中每个进程处理一个到 PostgreSQL 的连接。这样做是为了确保,在一个连接上发生崩溃不会导致整个池关闭。

主进程定义在main.c中。当客户端连接时,它会在自己的进程中进行处理,处理逻辑的实现在worker.hworker.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.hpool.c)中。

此 API 定义了池的功能,例如从池中获取连接并返回。进程之间没有顺序,因此新创建的进程可以在旧进程之前获得连接。

连接池在struct connection数据类型上的操作,定义在pgagroal.h中。

网络和消息

所有通信都使用message.h中定义的struct message数据类型进行了抽象。

读取和写出消息,在message.hmessage.c)文件中处理。

网络操作定义在network.hnetwork.c)中。

内存

每个进程都使用一个固定的内存块进行网络通信,该内存块在工作进程启动时分配。

这样,我们就不必为每条网络消息分配内存,更重要的是还要在使用结束后释放它。

内存接口定义在memory.hmemory.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.hremote.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 周期性调用

函数startclientserverstop可以访问以下信息

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的主进程支持信号SIGTERMSIGINTSIGALRM作为一种关闭机制。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.hprometheus.c中完成。

故障转移支持

pgagroal 可以在客户端无法写入的情况下,对 PostgreSQL 实例进行故障转移。

这是使用用户提供的外部脚本完成的。

实现在server.hserver.c中完成。

日志记录

基于atomic_schar锁实现的简单日志模块。

实现在logging.hlogging.c中完成。

协议

协议交互可以使用 Wiresharkpgprtdbg 进行调试。