PostgreSQL技术内幕(九)libpq通信协议

 

libpq通信协议是基于TCP/IP 协议的一套消息通信协议,它允许 psql、JDBC、PgAdmin等客户端程序传递查询给PostgreSQL后端服务器,并接收返回查询的结果。

在这次的直播中,我们为大家介绍了libpq通信协议的实现原理和执行机制,以下内容根据直播文字整理而成。

libpq通信协议简介

通信协议,是指通信双方按控制信息交换规则的标准、约定的集合,即网络上的传输规则。两个实体要成功地通信,必须“说同样的语言”。

libpq协议在TCP/IP模型网络分层中,属于应用层协议的一种。在进行libpq协议通信之前,要先完成连接的建立。libpq协议描述了交互所需的认证握手过程、数据请求应答过程与错误处理过程。

从交互层次来看,libpq通信协议包括建立连接、数据查询、终止链接阶三个阶段, 接下来我们将围绕这三个阶段不同的状态和模式展开阐述。

建立连接阶段(starup阶段)

用户使用libpq 驱动创建与数据库的连接,并发送授权信息,如果一切正常,服务端会反馈状态信息,连接成功创建。

 客户端connect和服务端accept过程示意图

如上图所示,libpq建立连接阶段大致分为三个步骤:连接建立阶段、加密协商阶段、认证协商阶段,其中连接建立阶段是tpc协议的内容,对应代码实现就是connect 和 accept函数。加密协商和认证协商是libpq通信建立连接阶段的重要流程。

建立连接

用户使用libpq创建连接的流程,从代码阶段主要分为三步:

1.创建 PGconn 类型连接对象 conn;

2.连接数据库(通过接口 PQconnectdb /PQconnectdbParams/ PQsetdbLogin);

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

用户通过 libpq 与 server 建立连接的过程,主要涉及到两种状态类型:

轮询状态类型PostgresPollingStatusType和连接状态类型ConnStatusType。

  • 轮询状态:

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

连接不是瞬间完成的,需要有一系列的处理过程,在此过程中的等待流程由轮询控制,是一个短暂的过程。PGRES_POLLING_FAILED 和 PGRES_POLLING_OK 为轮询终止的状态条件, PGRES_POLLING_READING,PGRES_POLLING_WRITING 为需要持续询问当前 conn 的状态条件。

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

  • 连接状态:

PQconnectPoll函数会为客户端连接推进连接状态机,为CONNECTION_MADE状态进行处理,这里的主要工作就是启动认证请求。

在连接过程的任何时候,都可以通过调用PQstatus 来检查连接的状态。如果此时调用返回CONNECTION_BAD,则连接过程失败;如果调用返回 CONNECTION_OK,则连接准备就绪。这两种状态都可以从 PQconnectPoll 的返回值中检测到。

在异步连接过程期间(并且仅在期间)也可能出现其他状态,比如writing ,代表客户端要给服务端发送认证、协商信息;reading 代表等待服务端返回信息包。

加密协商

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

加密协商阶段是可选的,只有开启GSSAPI认证或者SSL认证才会执行。在这一阶段,客户端调用PQconnectPoll函数中,ConnStatusType连接状态处于CONNECTION_NEEDED,然后调用connect函数去连接服务端,连接状态会转变为CONNECTION_STARTED。

这个时候,服务端postmaster会执行如下调用:StreamConnection函数会使用服务器端口创建与客户端的新连接,将 port->sock 设置为新连接的 FD。连接新建成功后,postmaster会调用BackendStartup为该客户端连接创建postgres后端服务子进程。

下一步,PQconnectPoll函数会尝试为此连接推进状态机,为CONNECTION_MADE状态进行处理,启动认证请求,并构建启动包。

如果编译宏参数ENABLE_GSS 或者USE_SSL开启,则进行加密协商。

加密协商过程如下:

client->server(协议版本信息)

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

server处理流程:

ServerLoop->BackendStartup->BackendInitialize->ProcessStartupPacket(处理加密)

认证协商

当加密协商阶段完成或跳过后,libpq 协议将开始进行认证阶段。认证阶段由 Startup message 消息开始,消息格式以消息长度开始,随后紧跟协议版本号,然后是键值对形式的连接信息,如用户名、数据库以及其他 GUC 参数和值。

前端发出Startup message 消息后,后端会进行认证应答,认证应答信息的类型为“R ”,其内容大致分为 3 种情况:完成认证(相当于不需要认证,此时用户不需要验证密码)、提供认证方式与所需的参数、认证错误。

前端通过认证应答信息提供的认证方式(如果有的话)向后端发送认证请求,

认证请求消息中包含后端所需要的认证参数,例如密码或密码的MD5 值等。

认证错误消息ErrorResponse 会导致后端直接关闭连接,停止认证协商。

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

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

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

  • 类型为“ S ”的 ParameterStatus :是一个 Key-value 对,进行参数设置;
  • 类型为“ K ”的 BackendKeyData:描述了一个取消请求的 Key,主要用户在开始阶段时 Cancel request 需要的 Key 值,用于在一个新建会话中中断另一个会话中阻塞操作;
  • 类型为“ Z ”的 ReadyForQuery:代表后端已经准备好开始一个新的数据请求。

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

连接建立流程逻辑示意图 

数据查询阶段(normal阶段)

数据查询阶段,客户端和服务端所有通信都通过消息流进行。消息的第一个字节标识消息类型,随后四个字节标识消息内容的长度(该长度包括这四个字节本身),具体的消息内容由消息类型决定。


服务端支持消息类型为PostgresMain 函数;客户端支持处理消息类型为pqParseInput3 函数。数据查询阶段常用的通信模式有三种,分别为Simple query、Extended query和Copy data。

Simple query模式:客户端通过 Query 消息发送一个文本命令给服务端,服务端处理请求,回复查询结果。查询结果通常包括两部分内容:结构和数据。结构通过 RowDescription 消息传递,包括列名、类型 OID 和长度等;数据通过 DataRow 消息传递,每个 DataRow 消息中包含一行数据。

每个命令的结果发送完成之后,服务端会发送一条CommandComplete 消息,表示当前命令执行完成。客户端的一条查询请求可能包含多条 SQL 命令,每个 SQL 命令执行完都会回复一条 CommandComplete 消息,查询请求执行结束后会回复一条 ReadyForQuery 消息,告知客户端可以发送新的请求。消息流如下:

Simple query消息流示意图 

Extended query模式:Extended query 协议将以上 Simple query 的处理流程分为若干步骤,每一步都由单独的服务端消息进行确认。Extended query 协议通常包括5个步骤,分别是 Parse、Bind、Describe、Execute 和 Sync,这里不再展开讲述。

Extended query 协议可以使用服务端的 prepared-statement 功能,即先发送一条参数化 SQL,服务端收到 SQL(Statement)之后对其进行解析、重写并保存,这里保存的 Statement 也就是Prepared-statement,可以被复用;执行 SQL 时,直接获取事先保存的 Prepared-statement 生成计划并执行,避免对同类型 SQL 重复解析和重写;随后,服务端会在适当的条件下缓存计划,以备后续复用。

PGQUERY_EXTENDED查询协议将一个SQL的执行过程拆分成三个层次,相邻的两个层次间抽象出statement和portal对象,每个层次允许单独重复调用,并且在当前连接的生命周期内,也允许再次调用,使整个SQL的执行过程具有了可重复利用性,对中间结果的保存减少了重复调用,节省了执行开销,也提高了执行速度。Extended query 完整消息流如下图所示:

 Extended query消息流示意图

Copy data 模式:为高效地导入/导出数据,libpq 支持 Copy 命令,Copy操作会将当前连接切换至一种截然不同的消息通信方式。

Copy data对应三种模式:copy-in 导入数据,对应命令 COPY FROM STDIN;copy-out 导出数据,对应命令 COPY TO STDOUT;copy-both 用于 walsender,在主备间批量传输数据。

以copy-in 为例,服务端收到 COPY 命令后,进入 COPY 模式,并回复 CopyInResponse。随后客户端通过 Copydata 消息传输数据,CopyComplete 消息标识数据传输完成,服务端收到该消息后,发送 CommandComplete 和 ReadyForQuery 消息,消息流如下图所示:

Copy data消息流示意图 

终止阶段

这一阶段流程相对简单,客户端请求结束后,可以主动发送消息断开连接。服务端接收到客户端的终止消息后,直接退出进程。


总结

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

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

本次分享为大家介绍了如何使用 libpq 建立与 PostgreSQL Server 的连接,并使用连接发送业务请求。对libpq协议感兴趣的同学可关注HashData公众号,了解更多libpq通信协议技术细节。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/20713.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

springboot项目实战-API接口限流

1.简介 对接口限流的目的是通过对并发访问/请求进行限速,或者对一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务、排队或等待、降级等处理。 1.1.为什么需要限流? 大量正常用户高频访问导致服务器宕机恶意用户高频访问导致服…

stm32(时钟和中断事件知识点)

一、复位和时钟控制(RCC) 复位 系统复位 当发生以下任一事件时,产生一个系统复位: 1. NRST引脚上的低电平(外部复位) 2. 窗口看门狗计数终止(WWDG复位) 3. 独立看门狗计数终止(IWDG复位) 4. 软件复位(SW复位) 5. 低功耗管…

MJ魔法AI逼真绘画

最近在玩MJ绘画,将一些经验分享给大家。 经验1:主体尽量放在前面 作图通用指令:主体描述词场景风格清晰度比例V5。MJ的逻辑是优先绘制前面的词,如果你的场景里面包含多个人或物。一定要把你希望在画面中出现的主体放在前面&#…

8-1-1、kuberbetes学习-service、deployment、ReplicaSet、pod

Kubernetes资源对象Pod、ReplicaSet、Deployment、Service之间的关系_CodingSoldier的博客-CSDN博客 Pod、ReplicaSet、Deployment、Service之间的关系如下图: deployment根据pod的标签关联到pod,是为了管理pod的生命…

个人微信开发API,微信机器人。

微信个人号二次开发,基于API开发可以有很多功能模块 各种知名SCRM系统、客服平台都是根据此API二次开发的。 在这里插入图片描述 好友管理: 添加好友、 删除好友、 修改备注、 创建标签、 获取好友列表、 检测僵尸粉 设置个人头像 同意添加好友 获取好…

基本概念【变量和数据类型和运算符、二进制和十进制、十进制转二进制 、二进制转十进制 】(一)-全面详解(学习总结---从入门到深化)

目录 变量和数据类型和运算符 二进制和十进制的转化 十进制转二进制 二进制转十进制 注释 标识符和关键字 关键字/保留字 变量(variable) 变量的分类和作用域 常量(Constant) 基本数据类型(primitive data type) 整型 浮点型(Floating Point Number) 字符型 …

我的2023年上半年总结

各位CSDN的uu们你们好呀,今天,小雅兰来分享一下这半年吧!!! 一、目标达成情况总结: 这半年算是比较充实吧。 一边学习学校的课程,同时自学C语言和数据结构与算法的知识,并且把所学习…

Kylin麒麟系统设置开机自动登录roo账户

1.安装麒麟系统后,默认root用户是不开启的,首先得设置root用户密码命令。 sudo passwd root 此时会要求输入密码,输入您当前用户登录密码即可。 2.以root权限修改 /usr/share/lightdm/lightdm.conf.d/60-kylin.conf 文件,如提示输入密码&am…

线性回归算法

什么是线性回归? 线性回归(Linear regression)是一种利用线性函数对自变量(特征)和因变量之间的关系进行建模的方法。线性回归是机器学习中一种广泛使用的基本回归算法。含有有多个特征的线性回归称为多元线性回归。 …

​ 基于单片机智能温室大棚控制系统

功能介绍 以51单片机作为主控系统; DS18B20温度采集模块检测温度; 光敏电阻和ADC0832组成的光照检测模块; 土壤湿度检测模块检测土壤湿度; CO2检测模块检测CO2浓度; LCD1602显示模块显示测量值、 若温度小于温度最…

Spark(21):SparkStreaming之DStream入门

目录 0. 相关文章链接 1. WordCount 案例实操 1.1. 需求 1.2. 添加依赖 1.3. 编写代码 1.4. 启动程序并通过netcat发送数据 2. WordCount 解析 0. 相关文章链接 Spark文章汇总 1. WordCount 案例实操 1.1. 需求 使用 netcat 工具向 9999 端口不断的发送数据&#xf…

当我掉入计算机的大坑中时,遇到简单的题也很吃力,这可如何是好呢?

一支笔,一双手,一道力扣(Leetcode)做一宿!!! 一、分享自己相关的经历 我们可能经常听到这句话,人永远赚不到认知以外的钱,如果把它放到程序员行业来说,同样适…