KaiwuDBkaiwudb logo

KaiwuDB 技术博客专区

libpq 与 PostgreSQL 建立连接过程解析

2023-02-07

1、libpq简介

libpq 是 C 应用程序与 PostgreSQL 的接口。libpq 是一组库函数,允许客户端程序将访问请求传递到 PostgreSQL 后端服务器并接收这些访问请求的结果。libpq 同时也是 C++ 、 Perl 、 Python 、 Tcl 等应用程序接口的底层引擎。


因此,如果使用以上其中一个包,libpq 某些行为就非常重要,特别是使用 libpq 的应用程序时用户可以看到的行为。使用 libpq 的客户端程序必须包含头文件 libpq-fe.h,且必须与 libpq 库链接。



2、通过 libpq 创建连接的过程


用户使用 libpq 驱动创建与数据库的连接,用于应用程序执行查询并返回结果时,需要使用 libpq 驱动提供的 Connect 方法进行连接,包括 PQconnectdb 和 PQconnectdbParams 两个接口。


二者底层连接是相同的逻辑,区别在于创建连接的 url 传入方式不同。 PQconnectdb 的传入参数即连接的字符串,而 PQconnectdbParams 将连接的信息以列表的形式传入。应用使用 libpq 创建连接的流程,主要分为 3 步:


  • 创建 PGconn 类型连接对象 conn;

  • 连接数据库(通过接口 PQconnectdb 或 PQconnectdbParams );

  • 判断连接对象 conn 的状态,若为 CONNECTION_OK ,则连接成功。




3、frontend 与 backend 建立连接的流程

Client 通过 libpq 与 server 建立连接的过程,主要涉及到 3 个状态图:


  • PostgreSQL 建立连接轮询状态图,驱动建立连接的流程;

  • PostgreSQL 的连接状态图,驱动建立连接的流程;

  • PostgreSQL 设置环境变量状态图( protocol version  <  3 )。


(1) PostgreSQL建立连接轮询状态图



image.png



轮询主要是用于等待 conn 对象创建 socket 、写入连接参数、等待 server 返回结果、等待连接认证等。此过程中 conn 对象已经创建,但未完成与 server 的服务连接过程。


connect 过程不是瞬间完成的,需要有一系列的处理过程,在此过程中的等待流程由轮询控制,是一个短暂的过程。轮询的所有状态定义如下:


image.png


用户需要创建连接时,libpq 使用轮询的方式查看当前轮询的状态。涉及到的几个状态包括 PGRES_POLLING_FAILED, PGRES_POLLING_READING,PGRES_POLLING_WRITING, PGRES_POLLING_OK。


PGRES_POLLING_FAILED 和  PGRES_POLLING_OK 为轮询终止的状态条件, PGRES_POLLING_READING,PGRES_POLLING_WRITING 为需要持续询问当前 conn 的状态条件。


在空的 conn 对象建立后,轮询进入初始状态 PGRES_POLLING_WRITING,调用 PQconnectPoll 询问到是否需要等待 conn 建立完成。若仍需等待,则继续等待和状态轮询直到连接建立完成,否则错误返回。


建立连接轮询的接口如下


image.png

image.png

image.png


(2)PostgreSQL 的建立连接流程



conn 建立与 server 连接的过程,需要经过复杂的流程,主要分为 3 个阶段:


1、连接建立阶段

主要是建立 socket 对象,以及连接 socket,为后续的加密协商阶段和认证协商阶段提供通信通道。


2、加密协商阶段

加密协商阶段是在连接建立后进行的第一个阶段,为了保证后续的认证协商阶段中会话信息不会泄漏,需要先对连接进行通信加密。


在连接建立后前端发出的第一个通信包将开始加密协商阶段,加密协商阶段前端会发出 4 种类型的通信消息:Startup message、SSL encrypt request、GSS encrypt request 和 Cancel request。


其中 Startup message 和 Cancel request 表示前端不需要进行加密协商阶段,但后端可以根据配置给予对应的应答,允许进入认证协商阶段或关闭连接。


3、认证协商阶段

当加密协商阶段完成或跳过后,PostgreSQL 协议将开始进行认证阶段。认证阶段由 Startup message 消息开始。


前端发出 Startup message 消息后,后端会进行认证应答,认证应答信息的类型为“ R ”,其内容大致分为 3 种情况:


  • 完成认证(相当于不需要认证)

  • 提供认证方式与所需的参数

  • 认证错误


认证错误消息 ErrorResponse 会导致后端直接关闭连接,停止认证协商。前端通过认证应答信息提供的认证方式(如果有的话)向后端发送认证请求,认证请求消息中包含后端所需要的认证参数,例如密码或密码的 MD5 值等。


认证请求的类型为“ P ”,其内容需要根据上下文进行推断,例如之前认证应答消息中的认证方式为 MD5,则认证请求消息中的内容就为密码的 MD5 值。


前端向后端发送认证请求后,后端会再次根据认证请求中的内容进行认证应答,直到认证完成或认证错误。所以,认证阶段完成的标志是:后端发送的内容为认证完成的认证应答消息或者发送了 ErrorResponse 的认证错误消息。


当认证完成时,后端会在认证应答信息后发送一些其他协议,来通知前端一些必要的参数,其中有:

  • 类型为“ S ”的 ParameterStatus :是一个 Key-value 对,进行参数设置;

  • 类型为“ K ”的 BackendKeyData:描述了一个取消请求的 Key,主要用户在开始阶段时 Cancel request 需要的 Key 值,用于在一个新建会话中中断另一个会话中阻塞操作;

  • 类型为“ Z ”的 ReadyForQuery:代表后端已经准备好开始一个新的数据请求。


至此,一个建立连接的过程已经完全准备完成。建立连接的状态图如下:


image.png


conn 连接状态定义如下:


image.png


具体连接过程的接口定义如下:


image.png


该方法主要是进行整个连接流程的控制,包括了连接阶段、加密协商阶段和认证协商阶段。代码行数 1000+,此处不进行赘述,代码位置为 PostgreSQL REL9_5_25 的:./src/interfaces/libpq/fe-connect.c


(3)PostgreSQL 设置环境变量流程


当建立连接的状态图完成认证之后,此时连接已经建立成功,达到状态 CONNECTION_AUTH_OK。


此时,需要考虑 libpq 的协议版本兼容性,如果协议版本>= 3,进入 CONNECTION_OK 状态,完成连接的创建;如果协议版本< 3,进入 CONNECTION_SETENV 状态,对 connection 对象 conn 进行参数的设置,设置成功之后进入 CONNECTION_OK 状态,并返回。


CONNECTION_SETENV 设置环境变量的流程中,也有一个状态机进行设置环境变量的流程控制,状态图如下:


image.png


设置环境变量流程中的状态定义如下:

/* PGSetenvStatusType defines the state of the PQSetenv state machine */
/* (this is used only for 2.0-protocol connections) */typedef enum{
    SETENV_STATE_CLIENT_ENCODING_SEND,  /* About to send an Environment Option */    
    SETENV_STATE_CLIENT_ENCODING_WAIT,  /* Waiting for above send to complete */    
    SETENV_STATE_OPTION_SEND,   /* About to send an Environment Option */    
    SETENV_STATE_OPTION_WAIT,   /* Waiting for above send to complete */    
    SETENV_STATE_QUERY1_SEND,   /* About to send a status query */    
    SETENV_STATE_QUERY1_WAIT,   /* Waiting for query to complete */    
    SETENV_STATE_QUERY2_SEND,   /* About to send a status query */    
    SETENV_STATE_QUERY2_WAIT,   /* Waiting for query to complete */    
    SETENV_STATE_IDLE
} PGSetenvStatusType;


环境变量的设置,主要是发送请求 set client_encoding,设置其他的变量 options,查询服务端 version 并设置 server_version、查询服务端设置的 client_encoding 并更新到连接对象 conn。设置环境变量的状态控制接口定义为:


image.png


代码行数 350+,此处不进行赘述,代码位置为 PostgreSQL REL9_5_25 的:./src/interfaces/libpq/fe-protocol2.c


总结

通过 libpq 与 PostgreSQL 建立连接是一个比较复杂的过程,主要通过 libpq 所在的 client 端进行驱动:发起请求,等待响应。


在建立连接轮询状态机、建立连接流程状态机和设置环境变量状态机中,有些状态会存在多次转换以完成连接建立的过程。经过连接建立、加密协商、认证协商三个阶段之后,一个连接到 PostgreSQL 的 PGconn 连接对象就准备完成,应用程序可以通过该对象进行后续各种业务的执行,向 Server 发起请求,并解析返回结果。


使用 libpq 建立与 PostgreSQL Server 的连接,并使用连接发送业务请求,详情参考示例>>https://www.postgresql.org/docs/9.5/libpq-example.html


免费体验 KaiwuDB 全新功能

立即体验

关于我们
联系我们

KaiwuDB B站

KaiwuDB
B站

KaiwuDB 微信公众号

KaiwuDB
微信公众号

© 上海沄熹科技有限公司 Shanghai Yunxi Technology Co., Ltd.    沪ICP备2023002175号-1
400-870-1188
1V1 方案咨询
marketing@kaiwudb.org.cn